7.7 KiB
Docker Deployment Guide
Deploy dashboard-cpsp with Docker Compose. Layout mirrors the legacy dashboard/ stack (Postgres + API + Nginx frontend), adapted for Django + Gunicorn and a separate cron container.
Architecture
┌──────────────────────────────────────────────────────────────┐
│ Host Server │
│ │
│ ┌─────────────┐ /api/* ┌──────────────┐ │
│ │ frontend │────────────►│ api │ │
│ │ (Nginx) │ │ (Django) │ │
│ │ port 80 │ │ port 8000 │ │
│ └─────────────┘ └──────┬───────┘ │
│ │ │
│ ┌─────────────┐ ▼ │
│ │ cron │────────────► ┌──────────────┐ │
│ │ (same image)│ │ database │ │
│ └─────────────┘ │ (PostgreSQL) │ │
│ │ port 15432* │ │
│ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
* 15432 on host → 5432 in container (SSH tunnel / DBeaver)
| Container | Image / build | Host port | Role |
|---|---|---|---|
| frontend | docker/Dockerfile.web |
80 → 80 | React SPA + Nginx API proxy |
| api | docker/Dockerfile.api |
18000 → 8000 | Gunicorn, migrations, static |
| cron | same as api | — | Scheduled management commands |
| database | postgres:16-alpine | 15432 → 5432 | PostgreSQL |
Prerequisites
- Docker 20.10+
- Docker Compose v2+
- Ports available: 80 (UI), 18000 (direct API), 15432 (Postgres on localhost)
docker --version
docker compose version
Quick start
cd dashboard-cpsp
cp docker/.env.example .env
# Edit .env — set SECRET_KEY and DB_PASSWORD
docker compose up -d --build
Production (empty database, no demo data):
docker compose exec api python manage.py bootstrap_admin
Uses BOOTSTRAP_ADMIN_USER / BOOTSTRAP_ADMIN_PASSWORD from .env (default in docker/.env.example: admin / Pr04dm1n). Optionally set BOOTSTRAP_STAFF_USER / BOOTSTRAP_STAFF_PASSWORD to create a staff login at the same time. Then log in and create sites, kandang, and cycles in Pengaturan.
Dev / demo (Sukawarna sample cycles):
docker compose exec api python manage.py seed_demo
Logins after seed: admin / admin123, staff / staff123. Do not run seed_demo on production.
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://dashboard.example.com) |
CORS_ALLOWED_ORIGINS |
Same as above if the SPA is on a different origin |
KARUNG_WEB_ADMIN_BASE_URL |
External karung service; default http://host.docker.internal:5000 reaches the host from containers |
BOOTSTRAP_ADMIN_USER |
Superadmin username for bootstrap_admin (default admin) |
BOOTSTRAP_ADMIN_PASSWORD |
Superadmin password for bootstrap_admin (required for production bootstrap) |
BOOTSTRAP_STAFF_USER |
Optional staff username; leave empty to skip staff creation |
BOOTSTRAP_STAFF_PASSWORD |
Staff password (required when BOOTSTRAP_STAFF_USER is set) |
SITE_API_KEY |
Optional fixed API key for scripts (hashed at rest) |
Wagtail admin is proxied at http://localhost/admin/ (through Nginx → Django).
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 newdashboard -d newdashboard
# From host (port 15432)
psql -h localhost -p 15432 -U newdashboard -d newdashboard
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 15432 mapping.
Cron jobs
The cron service runs:
- Every 10 min —
sync_iot_from_api(IoT panel, real-time — not gated by publish cutoff) - At
DASHBOARD_PUBLISH_HOUR:DASHBOARD_PUBLISH_MINUTEdaily —sync_karung_from_web_adminthenrecompute_kpi_rollups(entrypoint renders cron from these env vars; default 17:00)
Dashboard reads hide today's KPI/counting/weight/karung data until the configured publish time WIB. IoT panel is excluded and updates live every 10 minutes.
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
Ensure api is healthy and both services share newdashboard-network:
docker compose ps
curl http://127.0.0.1:18000/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.
Reset database (destructive)
docker compose down -v
docker compose up -d --build
docker compose exec api python manage.py bootstrap_admin
Security notes
- Do not commit
.envwith real secrets. - Use strong
SECRET_KEYandDB_PASSWORDin production. - Restrict database port
15432to 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