Deployment Architecture
:::info Status
Implemented. Source: docker-compose.yml, Caddyfile, DEPLOY.md.
:::
Production runs as a fixed set of Docker Compose services on one server, fronted by Caddy for TLS termination and routing.
Compose services
| Service | Role |
|---|---|
postgres | Database, pod-network only, no published port to the internet |
website, admin-panel, super-admin-panel, documentation-site | Nginx serving each frontend's production build |
migrate | One-shot: runs alembic upgrade head, must exit 0 before fastapi-backend starts (service_completed_successfully dependency) — migrations never run at app startup |
fastapi-backend | The backend, Uvicorn on :8082 |
emqx | Self-hosted MQTT broker for device control |
icecast | Audio streaming server — no published host port; only reachable through Caddy's /stream/* route |
otel-collector, tempo, prometheus | Observability pipeline — all internal-only, no published host port |
grafana | Traces/metrics viewer for the above — the one service in that group actually reached by a person, via its own subdomain (grafana.azan360.com) |
Release process
Deployments are triggered by git tags (semantic versioning, v<major>.<minor>.<patch>),
via a GitHub Actions workflow that SSHs into the server:
Manual trigger is also available (Actions → Deploy → Run workflow, optional tag input; empty deploys the latest tag).
Rollback
# Deploy an older tag
Re-running the same deploy flow against an earlier tag rolls the containers
back; database migrations are forward-only (see
Database — migrations), so a rollback that depends on
reverting schema changes needs a manually written down-migration — check
DEPLOY.md §Rollback before assuming a tag rollback alone is sufficient.
Full operational detail
Broker sizing/HA, stream-gate rollout, recipient fan-out rollout, certificate renewal, and troubleshooting live in Deployment and Operations — this page covers only the static topology.