7.2 KiB
Docker Deployment Guide
Deploy dashboard-cpsp-executive (HQ) with Docker Compose. Same layout as site dashboard-cpsp (Postgres + API + Nginx frontend + cron), with HQ ports and city-edge syncs off by default.
Architecture
┌──────────────────────────────────────────────────────────────┐
│ HQ Host Server │
│ │
│ ┌─────────────┐ /api/* ┌──────────────┐ │
│ │ frontend │────────────►│ api │ │
│ │ (Nginx) │ │ (Django) │ │
│ │ port 80 │ │ port 8000 │ │
│ └─────────────┘ └──────┬───────┘ │
│ │ │
│ ┌─────────────┐ ▼ │
│ │ cron │────────────► ┌──────────────┐ │
│ │ (same image)│ │ database │ │
│ └─────────────┘ │ (PostgreSQL) │ │
│ │ port 15433* │ │
│ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
* 15433 on host → 5432 in container (SSH tunnel / DBeaver)
Host API debug port: 18001 → 8000 (site app uses 18000 / 15432)
| Container | Image / build | Host port | Role |
|---|---|---|---|
| frontend | docker/Dockerfile.web |
80 → 80 | React SPA + Nginx API proxy |
| api | docker/Dockerfile.api |
18001 → 8000 | Gunicorn, migrations, static |
| cron | same as api | — | KPI rollups (+ optional edge syncs) |
| database | postgres:16-alpine | 15433 → 5432 | PostgreSQL |
Prerequisites
- Docker 20.10+
- Docker Compose v2+
- Ports available: 80 (UI; set
FRONTEND_PORT=8080in.envif busy), 18001 (direct API), 15433 (Postgres on localhost)
docker --version
docker compose version
Quick start
cd dashboard-cpsp-executive
cp docker/.env.example .env
# Edit .env — set SECRET_KEY and DB_PASSWORD
docker compose up -d --build
API entrypoint runs migrate + bootstrap_admin on every start (needs BOOTSTRAP_ADMIN_PASSWORD in .env).
Production (empty database, no demo data): log in with the bootstrap admin, then create users/sites via the UI. Do not run seed_demo on production.
Dev / demo (sample data):
docker compose exec api python manage.py seed_demo
Verify
docker compose ps
docker compose logs -f
curl http://localhost/health
curl http://localhost/api/v1/health/
Open http://localhost in a browser.
Configuration files (docker/)
| File | Purpose |
|---|---|
Dockerfile.web |
Frontend image (Vite + Nginx) |
Dockerfile.api |
Backend image (Django + Gunicorn) |
nginx.conf |
Nginx proxy config for frontend |
entrypoint-api.sh |
API container startup |
entrypoint-cron.sh |
Cron container startup |
crontab |
Fallback schedule; runtime regenerated from DASHBOARD_PUBLISH_* |
.env.example |
Compose env template → copy to project root .env |
compose.override.local.example |
Optional local Postgres port override |
Configuration
Compose reads variables from a root .env file. Template: docker/.env.example.
Important production values:
| Variable | Purpose |
|---|---|
SECRET_KEY |
Django secret — use a long random string |
DB_PASSWORD |
PostgreSQL password |
CSRF_TRUSTED_ORIGINS |
Must include your public UI origin (e.g. https://executive.example.com) |
CORS_ALLOWED_ORIGINS |
Same as above if the SPA is on a different origin |
BOOTSTRAP_ADMIN_USER |
Superuser username for bootstrap_admin (default admin) |
BOOTSTRAP_ADMIN_PASSWORD |
Superuser password (required) |
BOOTSTRAP_STAFF_USER |
Optional GM username; leave empty to skip |
BOOTSTRAP_STAFF_PASSWORD |
GM password (required when BOOTSTRAP_STAFF_USER is set) |
BOOTSTRAP_API_KEY |
Optional fixed API key for city inbound / scripts (hashed at rest) |
City-edge sync flags default to false at HQ. Enable only if this host also pulls directly from IoT / karung / chicken-counting edge APIs.
Management
# Start / stop
docker compose start
docker compose stop
docker compose restart
# Logs
docker compose logs -f api
docker compose logs -f frontend
docker compose logs -f cron
# Django shell
docker compose exec api python manage.py shell
# Database (psql)
docker compose exec database psql -U executive -d executive
# From host (port 15433)
psql -h localhost -p 15433 -U executive -d executive
Update after code changes
git pull
docker compose down
docker compose up -d --build
Rolling update (less downtime):
docker compose build
docker compose up -d --no-deps --build api
docker compose up -d --no-deps --build cron
docker compose up -d --no-deps --build frontend
Local DBeaver on Mac
cp docker/compose.override.local.example docker-compose.override.yml
Adds 127.0.0.1:5432:5432 while keeping the server’s 15433 mapping.
Cron jobs
The cron service runs:
- Every 10 min —
sync_iot_from_api/sync_chicken_counting_from_edge(no-op while sync flags are false) - At
DASHBOARD_PUBLISH_HOUR:DASHBOARD_PUBLISH_MINUTEdaily — optional karung sync thenrecompute_kpi_rollups
Karung 30s loop starts only when KARUNG_WEB_ADMIN_SYNC_ENABLED=true.
Logs: docker compose exec cron tail -f /var/log/cron.log
Troubleshooting
API unhealthy
docker compose logs api
docker compose exec api python manage.py migrate --plan
Frontend 502 on /api
docker compose ps
curl http://127.0.0.1:18001/api/v1/health/
Database connection errors
Check Postgres is healthy and credentials in .env match docker-compose.yml defaults.
Port conflicts
Change mappings in docker-compose.yml, e.g. "8080:80" for frontend. Site app already uses 80 / 18000 / 15432 on city hosts.
Reset database (destructive)
docker compose down -v
docker compose up -d --build
Security notes
- Do not commit
.envwith real secrets. - Use strong
SECRET_KEYandDB_PASSWORDin production. - Restrict database port
15433to localhost (already bound to127.0.0.1). - Put HTTPS in front of port 80 (reverse proxy + Let's Encrypt) for public deployment.
Docker deployment guide — dashboard-cpsp-executive