Files
2026-09-11 14:48:18 +07:00

7.2 KiB
Raw Permalink Blame History

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=8080 in .env if 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_MINUTE daily — optional karung sync then recompute_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 .env with real secrets.
  • Use strong SECRET_KEY and DB_PASSWORD in production.
  • Restrict database port 15433 to localhost (already bound to 127.0.0.1).
  • Put HTTPS in front of port 80 (reverse proxy + Let's Encrypt) for public deployment.

Docker deployment guide — dashboard-cpsp-executive