Infrastructure & deploy
One-line: the app runs in Docker behind Nginx on AWS EC2 (ap-south-1); GitHub Actions builds images to ECR and deploys via SSM; the marketing site is separate on Vercel.
This page describes production topology as understood in mid-2026. Infra changes often — treat specifics (IPs, security-group ids, migration heads) as a snapshot and verify against the infra repo and AWS before acting.
Runtime topology
Spaceship DNS
nexchool.in, www ─────────────► Vercel (landing-page, TLS by Vercel)
api / panel / * ─────────────► AWS EC2 ──► Nginx ──► Docker Compose
├── Flask API (gunicorn)
*.nexchool.in (tenant subdomains) ─► admin-web ├── Postgres (RDS)
├── Redis (cache + broker)
└── Celery workers- Marketing site (
nexchool.in,www) is Vercel-only — separate from the app.app.nexchool.inwas dropped; the app is reached via tenant subdomains (<slug>.nexchool.in). - App + API run on EC2 (account
774493573217, regionap-south-1), fronted by Nginx with TLS. The wildcard*record serves tenant subdomains through admin-web. - Database is PostgreSQL on RDS; Redis provides both the cache layer and the Celery broker.
CI/CD
- GitHub Actions build Docker images and push to ECR, then deploy to EC2.
- Deploys run via AWS SSM RunCommand (least-privilege role) — not SSH. Port 22 is closed; a manual shell is
aws ssm start-session. - The mobile
clientreleases on its own cadence (Expo OTA / store), gated by commit tokens ([ota]/[store-release]) — the server/admin-web deploy never triggers a mobile release. - Database migrations (Flask-Migrate / Alembic) are applied as part of the coordinated deploy; RDS snapshots are taken before risky ship steps.
Local development
Everything runs via Docker Compose from the infra repo (school-erp-infra/). See the maintained setup docs in the server/ repo for step-by-step. Environment variables for every service are documented alongside the code (.env.example files); never commit real secrets.
Security posture (as hardened)
- Internet-exposed DB access closed; DB reachable only from the app tier.
- AWS auth moved from static IAM keys → instance role; the old key deactivated. CI uses OIDC, not long-lived keys.
- SSH replaced by SSM; port 22 revoked.
- Secrets were history-swept across repos; use env vars / secrets manager, never source. See the security guardrails principles.
Known operational notes
ADMIN_WEB_BASE_URLmust be set in prod, or password-reset and one-click login links point atlocalhost.- SSE + gunicorn — real-time notification streams hold a worker thread; on a low worker/thread count this can starve request handling. Size the thread count accordingly (mitigated in prod). See Notifications.
- Email goes through AWS SES; exiting the SES sandbox is required for arbitrary-recipient prod email.
Related
- Overview · Services & repos
- Roadmap — reliability (P2) and deploy history