Principle
Dockerization should first change deployment mechanics, not product behavior. The first target is not a full rewrite, not Kubernetes, and not a new call router. The first target is:- same APIs
- same runtime config flow
- same call lifecycle
- same post-call result flow
- same one-call-per-worker capacity model
- repeatable container images
Phase 1: local Docker Compose
Purpose: make every repo runnable with one command for development and staging smoke tests. Services:| Service | Image/process |
|---|---|
cxb-api | FastAPI app |
cxb-console | Vite build served by nginx/Caddy |
cxb-core-worker | One CXB Core worker process |
cxb-dialler | Campaign loop + health server |
mongo | Local development MongoDB |
redis | Local development Redis |
minio | Local recording storage |
Phase 2: cheapest production Docker host
Purpose: replace systemd app management on a VPS with container management. Recommended host shape:MAX_CONCURRENT_CALLS=1. The local LB sets maxconn 1 per backend. The public URL remains:
Part of this “future” ingress scaffolding already exists in production. A production deployment fronts two fleets behind an HAProxy ingress at
calls.cxbridge.io, generated by render.sh and extended by add-fleet.sh. So Phase 2’s containerized per-host LB is the remaining step, not the HAProxy concept itself — the templated, brand-portable HAProxy config is already battle-tested. See Fleet capacity and WSS ingress.Phase 3: image registry and scripted deploys
Purpose: stop rsyncing code. Workflow:- Build images for CXB Core, CXB API, CXB Console, and CXB Dialler.
- Push to registry.
- Pull on target servers.
- Restart containers with health checks.
- Roll back by pinning a previous image tag.
staging and production.
Phase 4: capacity registry
Purpose: prepare for hundreds to thousands of call slots. Replace “poll every fleet URL” with a central registry: The registry must support:- worker registration with TTL
- atomic slot reservation
- reservation expiry if attach/start fails
- drain mode for deploys
- per-client or per-campaign quotas later
Phase 5: orchestration
Only introduce Kubernetes/Nomad/RKE2 when the operational pain justifies it:- many fleet hosts
- repeated rolling deploys
- need for autoscaling
- centralized service discovery
- stronger isolation between tenants