Docker Swarm
Docker Swarm is the recommended high-availability deployment for Docker environments. Swarm’s ingress routing mesh provides VIP load balancing and automatic failover without keepalived, and the stack file (docker-swarm.yml) declares replica counts, placement constraints, and health checks.
When to use Swarm
| Topology | Use when |
|---|---|
| Single-node Swarm | You want Swarm’s declarative stack model on one host (no HA, but ready to scale). |
| Multi-node Swarm + HA | You need HAProxy/Coraza redundancy and Valkey Sentinel failover across hosts. |
For a single-host non-HA deployment, the Quick Start Docker Compose flow is simpler. For Kubernetes, see Kubernetes.
Prerequisites
- A Linux host (or cluster) with Docker Engine and Swarm mode initialized
- Ports 80, 443, 3000, 8000, 8404 available (configurable)
- For multi-node: a container registry reachable from all nodes, or images manually loaded on each node
1. Initialize the Swarm
On the manager node:
docker swarm init
If you have multiple network interfaces, specify the advertise IP:
docker swarm init --advertise-addr <manager-ip>
Join worker nodes using the token printed by docker swarm init:
docker swarm join --token <worker-token> <manager-ip>:2377
Retrieve the join token later with docker swarm join-token worker.
2. Configure Environment
Copy the example environment file and edit it:
cp .env.example .env
At minimum set the secrets (SECRET_KEY, ADMIN_PASSWORD, DATAPLANE_API_PASSWORD). For HA, also set:
HA_ENABLED=true
SWARM_MODE=true
When SWARM_MODE=true, keepalived is skipped — Swarm’s ingress mesh already provides the VIP and failover. The backend reports swarm_mode in HA health/config responses so the UI shows the correct topology.
See the Quick Start environment reference for the full variable list, including the Swarm-specific section.
3. Make Images Available
Images must be present on every node that will run a replica. Either:
Option A — Use a registry (recommended for multi-node):
docker tag corex-api:latest registry.example.com/corex-api:v1.0
docker push registry.example.com/corex-api:v1.0
# Set in .env:
# SWARM_API_IMAGE=registry.example.com/corex-api:v1.0
# SWARM_HAPROXY_IMAGE=registry.example.com/corex-haproxy:v1.0
# SWARM_FRONTEND_IMAGE=registry.example.com/corex-frontend:v1.0
Option B — Load images on each node manually:
# On the build node:
docker save corex-api:latest corex-haproxy:latest corex-frontend:latest | gzip > corex-images.tar.gz
# Transfer and load on each Swarm node:
docker load < corex-images.tar.gz
4. Deploy the Stack
Single-node Swarm
docker stack deploy -c docker-swarm.yml corex
Multi-node Swarm with HA (via deploy.py)
The deploy.py script handles rsync, selective rebuilds, and docker stack deploy:
python3 deploy.py --target swarm \
--host <manager-ip> --user admin \
--stack-name corex \
--registry registry.example.com \
--image-tag v1.0
Manual HA stack deploy
# Ensure HA_ENABLED=true and SWARM_MODE=true in .env, then:
docker stack deploy -c docker-swarm.yml corex
5. Verify the Stack
docker stack services corex # list services and replicas
docker stack ps corex # list tasks (per-replica status)
docker service logs corex_api # view API logs
All services should show REPLICAS as x/x (e.g. 2/2). A 0/x replica count or FAILED task state indicates a scheduling or health-check failure — inspect with docker service ps --no-trunc <service>.
HA Behavior
When HA_ENABLED=true + SWARM_MODE=true:
| Component | Behavior |
|---|---|
| HAProxy | 2 replicas (SWARM_HAPROXY_REPLICAS). Swarm ingress mesh provides the VIP — no keepalived. |
| Coraza SPOA | 2 replicas (SWARM_CORAZA_REPLICAS), load-balanced by HAProxy. |
| Valkey | Primary + 1 replica (SWARM_VALKEY_REPLICA_REPLICAS) + 3 Sentinels (SWARM_SENTINEL_REPLICAS) for quorum failover. |
| Stick-table sync | HAProxy peers connect via tasks.corex DNS (dnsrr endpoint mode). |
Adjust replica counts in .env (the SWARM_*_REPLICAS variables) and redeploy.
Service URLs
After deployment, the services are available at:
| Service | URL | Notes |
|---|---|---|
| Web GUI (HTTP) | http://<host>:3000 | React frontend |
| Web GUI (HTTPS) | https://<host>:3443 | React frontend over TLS |
| API | http://<host>:8000 | FastAPI control plane |
| HAProxy (HTTP) | http://<host>:80 | Data plane ingress |
| HAProxy (HTTPS) | https://<host>:443 | Data plane ingress over TLS |
| HAProxy (HTTP/3) | https://<host>:443 | QUIC/HTTP3 over UDP (443/udp) |
| HAProxy Stats | http://<host>:8404 | Stats page (set HAPROXY_STATS_USER/PASS to enable auth) |
| Data Plane API | https://<host>:5555/v3 | HAProxy Data Plane API (when DATAPLANE_API_ENABLED=true) |
| HAProxy peer | tcp://<host>:10000 | Stick-table replication (dnsrr endpoint mode; open between instances) |
| CAPTCHA | http://<host>:3001 | Cap challenge service (when CAPTCHA enabled) |
| MCP Gateway | http://<host>:8089/mcp | Tools appear as corex-manager__* (when MCP_GATEWAY_ENABLED=true) |
| MCP Server | http://<host>:8082/mcp | Direct MCP server; requires Authorization: Bearer <COREX_MCP_TOKEN> |
| PostgreSQL | <host>:5432 | Database (bind to 127.0.0.1 in production) |
With the ingress mesh, any Swarm node accepts traffic on the published ports and routes to a healthy replica. The peer port (10000) uses endpoint_mode: dnsrr so HAProxy instances discover each other directly for stick-table sync.
Managing the Stack
docker stack services corex # list services
docker stack ps corex # list tasks
docker service logs corex_api # view logs
docker service scale corex_corex=3 # scale HAProxy to 3 replicas
docker stack rm corex # remove the stack
docker stack rm corex stops and removes all services and networks. Persistent data on bind mounts (under DATA_DIR) is preserved, but named volumes created by the stack are removed.
Deploy Script Options
The deploy.py Swarm-specific options:
| Option | Description | Default |
|---|---|---|
--target swarm | Use the Docker Swarm deploy flow | — |
--stack-name | Swarm stack name | corex |
--registry | Container registry URL for multi-node image distribution | — |
--image-tag | Docker image tag for rebuilt images | latest |
The script shares the same selective-rebuild change detection as the Docker target: a manifest of file hashes is compared against the last deploy, and only services whose files changed are rebuilt. See deploy.md in the corex_manager repo for full details.
Next Steps
- Kubernetes — Deploy via the Helm chart
- Listeners & Backends — Configure TLS, HTTP/2, QUIC, and load balancing
- Metrics & Logging — Monitor the HAProxy and WAF dashboards
- Audit & Snapshots — Track changes and revert config