Production cluster (Docker Swarm)
When one host isn’t enough — for uptime or for load — Crimson Haven runs as a Docker Swarm stack. Because the backend is stateless, you scale it freely; only the database needs care.
What Swarm gives you
Section titled “What Swarm gives you”- Ingress routing mesh — built-in L4 load balancing across replicas.
- Self-healing — failed containers are rescheduled automatically.
- Zero-downtime rolling updates with automatic rollback on failure.
The client and backend both ship Swarm-ready stack files (docker-stack.yml) with
deploy: blocks (replicas, restart policy, resource limits, stop-first updates).
Deploying the stacks
Section titled “Deploying the stacks”# On a manager node, once:docker swarm init
# The client (built + pushed to a registry your nodes can reach):docker build --build-arg VITE_API_BASE_URL=https://backend.example.com \ -t registry.example.com/crimson-client:1.0 .docker push registry.example.com/crimson-client:1.0CRIMSON_IMAGE=registry.example.com/crimson-client:1.0 \ docker stack deploy -c docker-stack.yml crimson-client
# The backend:docker stack deploy -c docker-stack.yml crimsondocker service ls # watch replicas convergeThe two rules for multiple backend replicas
Section titled “The two rules for multiple backend replicas”RUN_DB_SYNC=trueon exactly one replica. The periodic TMDB↔AniList mapping rebuild must run once, not N times. The reference stack pins it to a dedicatedapi-syncservice and setsfalseeverywhere else.- The same
PROXY_SECRETon every replica, so a signed link minted by one replica verifies on any other.
A third, if you use the cache worker: run the background ffmpeg downloader on one
dedicated worker service (RUN_CACHE_WORKER=true there, false elsewhere).
The database in production
Section titled “The database in production”The serving replicas are stateless; PostgreSQL is the stateful heart. The production reference uses:
- Patroni — PostgreSQL with automatic leader election + failover via etcd. (Watch
the loopback
pg_hbarule after a switchover — a documented gotcha in the backend’sdeploy/notes.) - PgBouncer — co-located transaction-mode pooling so the API tier can scale past
~8 replicas without exhausting database connections. Point
DATABASE_URLat PgBouncer’s:6432and leaveDB_PREPARE_THRESHOLDunset. - pgBackRest — encrypted backups to a single shared object-storage repository (e.g. Backblaze B2 via S3). Create the stanza once cluster-wide; the backup cron follows the current leader.
These are involved; the operational specifics live alongside the backend’s deploy/
directory. If that’s more than you need, a single well-backed-up PostgreSQL behind the
stack is perfectly legitimate.
Scaling the edge, not the core
Section titled “Scaling the edge, not the core”Remember the design: video bytes don’t flow through your stack. So scaling is mostly about the database and the stateless API tier — not bandwidth. The heavy lifting is on the free edge proxy (deploy to both Netlify and Cloudflare for redundancy) and in visitors’ browsers via the extension.
Dev vs prod environments
Section titled “Dev vs prod environments”The reference setup runs a parallel dev environment from the dev branches: a
single-replica stack with a bundled (isolated) PostgreSQL, on separate hostnames
(dev-backend.example.com, dev.example.com), so changes are tested before a release
ships to production. The CI/CD pipeline page wires this up.