# 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) ```bash docker --version docker compose version ``` ## Quick start ```bash 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): ```bash 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): ```bash docker compose exec api python manage.py seed_demo ``` Logins after seed: `admin` / `admin123`, `staff` / `staff123`. Do **not** run `seed_demo` on production. ### Verify ```bash 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` | Scheduled jobs (IoT 10 min, karung/KPI 17:00) | | `.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) | | `BOOTSTRAP_API_KEY` | Optional fixed API key for scripts (hashed at rest) | Wagtail admin is proxied at **http://localhost/admin/** (through Nginx → Django). ## Management ```bash # 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 ```bash git pull docker compose down docker compose up -d --build ``` Rolling update (less downtime): ```bash 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 ```bash 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 17:00) - **17:00 daily** — `sync_karung_from_web_admin` then `recompute_kpi_rollups` (feed use counts until 16:59, so karung sync runs at publish time) Dashboard reads hide today's KPI/counting/weight/karung data until 17:00 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** ```bash 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`: ```bash 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) ```bash docker compose down -v docker compose up -d --build docker compose exec api python manage.py bootstrap_admin ``` ## Security notes - Do not commit `.env` with real secrets. - Use strong `SECRET_KEY` and `DB_PASSWORD` in production. - Restrict database port `15432` 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