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 /training.
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 orscripts/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
RelayPipeChanneluses the fellow'sSWARM_LEADER_URL, so the relay path follows the tunnel automatically (verified end-to-end intests/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 thepoplong-poll pass cleanly through the tunnel.
Quick-tunnel URLs are ephemeral — they change whenever
cloudflaredrestarts, so re-exportSWARM_LEADER_URLand 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.ymlattachesfrontendto the externaldokploy-networkwith the aliassilentswarm-frontend, so Traefik resolves it by name./etc/dokploy/traefik/dynamic/silentswarm.ymldefines theweb→websecurerouters (HTTP→HTTPS redirect) withtls.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¶
The Helm chart in deploy/helm/silentswarm/ installs the control plane: the
leader as a one-pod StatefulSet on a ReadWriteOnce volume, Headscale beside
it in the same pod when the mesh is on, the nginx frontend as a Deployment,
and an Ingress per host. Fellows join from outside with the curl one-liner, as
everywhere else. Install, values, upgrades and backups:
Kubernetes (Helm).
Not a Deployment for the leader, and not more than one replica: the fellows'
connections, the relay and the job loops belong to one process, and a second
one on the same SQLite file would corrupt it.
Existing jobs do not repartition dynamically when the fellow count changes. Start a new job or sweep after the intended fellow set is ready.