EngineeringGetting started (local)

Getting started (local)

Run the full NexSchool stack locally in Docker — API, database, queues, and both Next.js frontends — then layer on migrations, seed data, and the Expo mobile app.

Prerequisites

The entire stack runs in Docker. You do not need Python, PostgreSQL, or Redis installed locally.

ToolVersionPurpose
Docker DesktopLatestRun all services via Compose
Docker Compose pluginv2Multi-service orchestration
Node.js20+Run frontends outside Docker (optional)
GitAnyClone the repo

Repository layout

The infra repo (school-erp-infra/) uses sibling path mounts — its Docker Compose volumes reference ../server, ../admin-web, and ../panel relative to school-erp-infra/. All repos must sit at the same directory level:

your-workspace/
├── school-ERP/          ← monorepo (this repo)
│   ├── server/
│   ├── admin-web/
│   ├── panel/
│   ├── client/
│   └── school-erp-infra/
└── (other projects)

Step 1 — Environment file

cd school-ERP/school-erp-infra
cp env/.env.local.example env/.env.local

Edit env/.env.local with your values:

# Database
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/school_erp

# Redis
REDIS_URL=redis://redis:6379/0
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0

# Flask
FLASK_ENV=development
JWT_SECRET=your-local-secret-change-this
SECRET_KEY=your-flask-secret

# Frontend → API
NEXT_PUBLIC_API_URL=http://localhost/api
NEXT_PUBLIC_GATEWAY_ORIGIN=http://localhost

# Firebase (admin-web push notifications — optional for local dev)
NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=

See /engineering/environment-variables for the full variable reference.

Step 2 — Start all services

From school-erp-infra/:

make dev
 
# Equivalent:
docker compose -f docker-compose.local.yml up --build

You can also start it from the monorepo root with make infra-dev.

Services & ports

ServicePortNotes
nginx80Gateway — all traffic enters here
api5001 (internal)Flask + Gunicorn
postgres5432PostgreSQL 16
redis6379 (internal)Celery broker
celery-workerBackground tasks
celery-beatScheduled tasks
admin-web3000 (internal)Next.js school admin
panel3001 (internal)Next.js super-admin

URLs after startup:

URLService
http://localhost/School admin (admin-web)
http://localhost/api/Flask API
http://localhost/api/healthHealth check
http://panel.localhost/Super-admin panel (see Step 3)
http://localhost:5001/api/API direct (bypasses nginx)
http://localhost:5432PostgreSQL direct

Step 3 — Panel subdomain (one-time)

The super-admin panel runs on panel.localhost. Add it to your /etc/hosts:

127.0.0.1 panel.localhost

Then open http://panel.localhost/.

Step 4 — Run database migrations

# Option A: via Docker exec
docker compose -f docker-compose.local.yml exec api flask db upgrade
 
# Option B: if running the API locally outside Docker
cd server
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
FLASK_APP=app:create_app flask db upgrade

Step 5 — Seed RBAC data

docker compose -f docker-compose.local.yml exec api python scripts/seed_rbac.py

This seeds:

  • Default permissions (all resource.action strings)
  • Default roles (admin, teacher, student, finance_staff)
  • Role-permission assignments

Step 6 — Mobile app (Expo)

The mobile app is not started by Docker Compose. Run it separately:

cd client
npm install
npm start        # Starts Expo dev server
# Scan QR with Expo Go app on your phone
# OR press 'i' for iOS simulator, 'a' for Android emulator

Point the app at your local API via client/.env (or client/config/):

EXPO_PUBLIC_API_URL=http://192.168.x.x/api   # Your LAN IP (for a physical device)
# OR
EXPO_PUBLIC_API_URL=http://localhost/api      # For emulators

Running individual services without Docker

Backend only

cd server
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
 
export FLASK_APP=app:create_app
export FLASK_ENV=development
export DATABASE_URL=postgresql://localhost/school_erp
export REDIS_URL=redis://localhost:6379/0
export JWT_SECRET=dev-secret
 
flask run --port 5001

Admin web only

cd admin-web
npm install
npm run dev      # http://localhost:3000

Panel only

cd panel
npm install
npm run dev      # http://localhost:3001

Common commands

Makefile shortcuts from the monorepo root:

make infra-dev          # local stack (docker compose local up)
make infra-prod         # build production-like stack from source
make infra-deploy-prod  # git pull apps + prod compose up --build
make infra-deploy-ecr   # pull ECR images + up (AWS)

Stopping services (from school-erp-infra/):

# Stop all
docker compose -f docker-compose.local.yml down
 
# Stop and wipe database volumes
docker compose -f docker-compose.local.yml down -v

Troubleshooting

ProblemSolution
Port 80 in useStop Apache/Nginx/other on host, or change the nginx port in the compose file
panel.localhost not resolvingAdd 127.0.0.1 panel.localhost to /etc/hosts
DB migrations failCheck DATABASE_URL in env, ensure the postgres container is healthy
CORS error in browserEnsure NEXT_PUBLIC_API_URL matches what the browser can reach
Hot reload not workingEnsure volume mounts in the compose file point to the correct sibling paths
Celery tasks not runningCheck the redis connection and CELERY_BROKER_URL in env
API returns 403 on all routesRun seed_rbac.py; check JWT_SECRET matches between env and code