Skip to main content
Schoolyi
School librarian arranging books and planning cards in a bright library

Browse docs

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 stepSymptomWhere it surfaces
Daily school cron never scheduledAutopay, dunning, reminders, and scheduled notices silently never runStale job heartbeats on the operations dashboard
SMTP left unconfiguredNo receipts, no magic links, no admissions correspondence/settings/email-test fails before anyone notices in production
NEXTAUTH_URL does not match the public hostnameSign-in redirects fail or sessions drop immediatelySign-in loop at /signin
mysql-client absent on the serverNo pre-migrate dump, so a bad migration has nothing to roll back toA 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.