Skip to content

Deployment

SilentSwarm supports local processes, Docker Compose, remote bootstrap, and a Kubernetes-oriented topology. All modes use the same leader/fellow contracts.

Local Processes

swarm start-cluster --fellows 2
swarm nodes
swarm fellows
swarm stop-cluster

The local leader defaults to http://localhost:8080 and logs to .swarm/runtime-logs/.

Docker Compose

docker compose --profile local-fellow up -d --build
swarm --leader-url http://localhost:3000 nodes

The frontend service proxies the leader through port 3000 and redirects the browser to /ops/join-v2.

Temporary Public Tunnel

Expose the leader on a public URL (Cloudflare Quick Tunnel, no account) so remote fellows can register and exchange activations through the leader relay (tunnel transport — used when fellows can't reach each other directly).

# 1. Start leader + frontend (nginx) + cloudflared tunnel
docker compose --profile tunnel up -d --build
docker compose --profile tunnel logs -f tunnel        # prints: https://<name>.trycloudflare.com
# 2. Tell the cluster its public URL (used for register, heartbeat, AND the relay)
export SWARM_LEADER_URL=https://<name>.trycloudflare.com
docker compose --profile tunnel up -d                  # restart leader with the URL set
  • The CLI targets the tunnel with swarm --leader-url "$SWARM_LEADER_URL" nodes.
  • Remote fellows are started with SWARM_LEADER_URL=$SWARM_LEADER_URL (env or scripts/start_fellow.sh <id> "$SWARM_LEADER_URL"); they register, heartbeat, and route training activations/gradients through "$SWARM_LEADER_URL"/api/v1/relay/....
  • The torch runtime's RelayPipeChannel uses the fellow's SWARM_LEADER_URL, so the relay path follows the tunnel automatically (verified end-to-end in tests/integration/test_tunnel_relay_transport.py).
  • nginx is configured for the relay (client_max_body_size 256m, buffering off, long-poll read timeouts) so multi-MB activations and the pop long-poll pass cleanly through the tunnel.

Quick-tunnel URLs are ephemeral — they change whenever cloudflared restarts, so re-export SWARM_LEADER_URL and restart the leader/fellows after each tunnel restart. For a stable URL use a named Cloudflare tunnel.

Stable Public Domain (host reverse proxy)

For a permanent public URL with a real, auto-renewing TLS certificate, serve the frontend through the host's reverse proxy instead of a tunnel. The production deployment of silentswarm.nexpatch.ai does this via the host's shared Traefik/Dokploy ingress (Let's Encrypt):

# .env (repo root, gitignored)
SWARM_LEADER_URL=https://silentswarm.nexpatch.ai
SILENTSWARM_FRONTEND_PORT=127.0.0.1:3300   # localhost-only; ingress fronts it

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build leader frontend
  • docker-compose.prod.yml attaches frontend to the external dokploy-network with the alias silentswarm-frontend, so Traefik resolves it by name.
  • /etc/dokploy/traefik/dynamic/silentswarm.yml defines the webwebsecure routers (HTTP→HTTPS redirect) with tls.certResolver: letsencrypt. Traefik obtains/renews the cert over the HTTP-01 challenge — no manual cert handling.
  • A DNS A record for the host (e.g. Strato) must point at the server's IPv4 so the ACME challenge resolves.

Unlike the tunnel, SWARM_LEADER_URL is stable, so leader and fellows do not need re-pointing after restarts.

Kubernetes-Oriented Topology

flowchart TB
    Ingress[Ingress or port-forward] --> LeaderSvc[leader Service :8080]
    LeaderSvc --> Leader[leader Deployment]
    Leader --> State[(join_state.sqlite3 / PVC)]
    Leader --> FellowSvc[fellows headless Service]
    FellowSvc --> F0[fellow-0]
    FellowSvc --> F1[fellow-1]
    F0 -- ZMQ or relay --> F1
    F0 -- heartbeat/logs/WS --> Leader
    F1 -- heartbeat/logs/WS --> Leader

Recommended resources:

  • Leader Deployment and Service on port 8080.
  • Fellow StatefulSet when stable identity is useful.
  • Headless fellow Service for direct ZMQ discovery.
  • Persistent storage for SWARM_JOIN_STATE_DB and optional checkpoints.
  • Network policy that allows leader HTTP/SSE/WebSocket and selected fellow ZMQ ports.

Existing jobs do not repartition dynamically when the fellow count changes. Start a new job or sweep after the intended fellow set is ready.