Deploy & admin
Production deployment
The Ubuntu deploy pipeline end to end - env merge, pre-migrate dump, prisma migrate deploy, seeds, systemd restart - and the two scheduled jobs, only one of which installs itself.
Deploy & admin guide for day and boarding schools.
Last updated August 29, 2026
A production deploy targets one Ubuntu server that runs both the Next.js application and its MySQL database, with Caddy in front terminating TLS and proxying to the app on localhost. deploy/update.sh performs the whole sequence: reset the repository to the deploy branch, merge .env.prod into .env, npm ci, check the database is reachable, take a pre-migrate dump, apply migrations, build, then restart the systemd unit. The default path preserves existing data.
Two people are usually involved, and it is worth naming them before go-live. Whoever holds the server account owns the environment file, the TLS hostname, and the cron entries; the school Platform Admin owns everything configured inside the product afterwards. Neither can finish the job alone, and the handover point is the moment /settings/email-test sends successfully.
Migrations and seeds
- npx prisma migrate deploy - forward-only; runs before build on every update
- Default seed - db:seed:users creates missing base logins on every deploy
- Full demo - npm run demo (SSH + db:seed:all) only when explicitly needed; never as routine prod update
Post-deploy ops
- Schedule scripts/run-school-cron.sh daily (e.g. 05:30 IST) with FEES_CRON_SECRET - fee autopay, dunning, homework due reminders, scheduled notices, retention purges, and alert evaluation
- Add ADMISSIONS_CRON_SECRET to the same entry if you want admission offers to expire on their own
- Validate SMTP on staging via /settings/email-test before go-live receipts and magic links
- Online fees - FEES_ONLINE_PAYMENT_MODE=simulated for staging; set razorpay, stripe, or telr with the matching keys and webhook secret when enabling live checkout; disabled when unset
What breaks if a step is skipped
| Skipped step | Symptom | Where it surfaces |
|---|---|---|
| Daily school cron never scheduled | Autopay, dunning, reminders, and scheduled notices silently never run | Stale job heartbeats on the operations dashboard |
| SMTP left unconfigured | No receipts, no magic links, no admissions correspondence | /settings/email-test fails before anyone notices in production |
| NEXTAUTH_URL does not match the public hostname | Sign-in redirects fail or sessions drop immediately | Sign-in loop at /signin |
| mysql-client absent on the server | No pre-migrate dump, so a bad migration has nothing to roll back to | A warning in the deploy output that is easy to scroll past |
- DATABASE_URL - MySQL connection string
- NEXTAUTH_URL / NEXT_PUBLIC_SITE_URL - canonical public URL
- NEXTAUTH_SECRET - session encryption secret
- SMTP_* - fee receipts, admissions, magic-link, and notifications
- FEES_ONLINE_PAYMENT_MODE - simulated | razorpay | stripe | telr | disabled
- FEES_CRON_SECRET - auth for scripts/run-school-cron.sh fee jobs
- Deploy keys and SSH - see tracked .env templates for DEPLOY_* variables
Common questions
Quick answers in plain language.
Does a routine update wipe the demo or live data?+
No. Deploy and update run db:seed:users after migrate, and that script only creates the base admin, staff, and sales logins when those usernames are missing. Existing users, passwords, and school data stay as they are. SKIP_DB_SEED=1 still blocks the full demo seed. Full demo data needs npm run db:seed:all on the server or the Seed Data screen. DEPLOY_SEED=skip skips the base-user check too.
Which scheduled jobs does deploying actually install?+
Only the examination timer. deploy/update.sh writes and enables schoolyi-exams-cron.timer at 05:30 daily. The daily school job in scripts/run-school-cron.sh is not installed for you, so fee autopay, dunning, homework reminders, scheduled notices, retention purges, and alert evaluation do nothing until somebody adds it to cron.
The build was killed on the server. What went wrong?+
Almost always memory. deploy/infer-build-heap.inc.sh derives the Node heap from RAM rather than RAM plus swap, and a small host can still be tight. Set BUILD_NODE_HEAP_MB explicitly, or enable swap with DEPLOY_ENSURE_SWAP=1, then re-run the update.
Related searches
School leaders and IT teams often search for: Ubuntu school software deploy, and school SaaS hosting.

