# ADR-014: Docker-first deployment model - **Status**: Accepted - **Date**: 2026-05-26 - **Deciders**: alkdev ## Context The hub needs a deployment model. Several architecture questions were left open about how the hub relates to its infrastructure dependencies (Postgres, Redis), how configuration is managed across environments, and what assumptions the architecture can make about the runtime environment. The previous iteration (alkhub_ts) was designed for a specific production infrastructure with specific host IPs and manual setup. The hub (`@alkdev/hub`) is designed to be a generalized, OSS-first project that others can deploy. ## Decision The hub assumes Docker as its primary deployment model. This resolves several open questions: 1. **Postgres and Redis run in the same Docker network** — No cross-network TLS needed between hub and its data stores. `PostgresConfig.ssl` can remain `boolean` for v1 (same-network communication doesn't require TLS between containers). TLS termination happens at the reverse proxy (nginx/caddy) for external traffic. 2. **Configuration is encrypted files + Docker secrets** — The `alkhub-config` CLI encrypts config values. CI/CD doesn't need the master key because config files are pre-encrypted and mounted at runtime. The master key is a Docker secret provisioned by the operator. This eliminates the "how does CI/CD get the key?" problem (OQ-21). 3. **Docker Compose handles environment variation** — Dev vs. prod differences are handled via different Docker Compose files and different mounted config files. No overlay config system needed (OQ-22). 4. **Single-container restart is sufficient for v1** — No hot spare or zero-downtime restart needed. Docker's restart policy handles failures. Connection draining and session transfer are Phase 2 concerns (OQ-35). 5. **Startup observability via `/health` + Docker logs** — The `/health` endpoint with step-level progress and structured JSON logging to stdout is sufficient. Docker's `HEALTHCHECK` directive and log aggregation handle the rest. No pub/sub startup events needed (OQ-36). 6. **Migrations block startup** — Single-container deployment means the hub must have a consistent schema before serving. Background migrations require schema version negotiation that adds complexity without benefit in a single-container model (OQ-34). 7. **Redis is hub-internal** — Redis runs in the same Docker network as the hub. Spokes connect via WebSocket (not Redis), so Redis topology is a deployment detail, not an architectural concern (OQ-37). ## Consequences **Positive**: Deployment model is explicit and consistent. Config, networking, and observability all assume Docker conventions. Open questions about SSL between hub and Postgres, CI/CD key management, config overlays, and zero-downtime restarts are resolved by the Docker model. **Negative**: Docker is a hard dependency for production deployment. Developers who want to run the hub outside Docker must provide their own Postgres and Redis, and handle TLS and config management themselves. This is acceptable — the Docker model is the documented production path. ### Open questions resolved by this decision | OQ | Resolution | |----|-----------| | OQ-21 | CI/CD doesn't need the master key — config files are pre-encrypted and mounted at runtime; master key is a Docker secret | | OQ-22 | No overlay config needed — Docker Compose handles environment variation via mounted volumes | | OQ-34 | Block migrations at startup — single-container model requires consistent schema before serving | | OQ-35 | Single-container restart is v1 — Docker restart policy handles failures; zero-downtime is Phase 2 | | OQ-36 | `/health` + Docker logs is sufficient — no pub/sub startup events needed | | OQ-37 | Redis is hub-internal in Docker — same network, not an architectural concern | ### Open questions narrowed by this decision | OQ | Narrowing | |----|-----------| | OQ-23 | `PostgresConfig.ssl: boolean` is sufficient for same-network Docker deployment. TLS between containers is a deployment concern, not an app config concern. |