DocsInfrastructure
Docker Swarm
Run applications as a service with replicas across several machines, using the orchestrator already built into Docker — no Kubernetes.
- What for
- Several machines under one panel
- Shape
- Applications as a service + replicas
- Who
- Owner (init/leave/node), admin sees status
Swarm or remote servers?
| Remote server (SSH) | Swarm | |
|---|---|---|
| Shape | The panel controls another daemon over SSH | One cluster; Docker's scheduler places tasks |
| Application | A single container on the chosen server | N replicas that can be spread across several nodes |
| Domain | Proxy per server (or a host port) | Traefik on the manager with the swarm provider |
| Good for | Separate environments, different regions | Adding capacity for the same application |
You can use both at once: the aoox host becomes the swarm manager, while a remote server keeps serving other applications separately.
Flow
- 01initthe host becomes a manager, the aoox-swarm overlay is created
- 02joinother nodes join using a token from the panel
- 03servicean application is switched to deploy mode service + replicas
- 04rollingthe daemon replaces tasks gradually, rolling back on failure
Enabling swarm
Settings → Docker Swarm → Init swarm (owner)
Fill in Advertise address if the host has more than one IP — this is the address other nodes will use to reach the manager. Leave it blank for auto-detection.
aoox turns the host daemon into a manager, creates an attachable overlay network called
aoox-swarm, joins the proxy and managed databases to it, then enables Traefik's swarm provider.Join other nodes
The Swarm card shows the full join command with its token — run it on the machine that will join (Docker already installed, ports
2377,7946, and4789/udpopen between nodes):docker swarm join --token SWMTKN-1-… <manager-ip>:2377Worker and manager tokens differ; the token is only shown to the owner. A joined node appears in the node list a few seconds later.
Make sure the registry is reachable from other nodes
Nodes pull images themselves from the local registry. Swarm status warns if
REGISTRY_PUBLIC_HOSTis stilllocalhost— other nodes won't be able to pull from it. Set it to a reachable hostname/IP (with TLS orinsecure-registries) — see Registry.
Nodes over a VPN (WireGuard)
Nodes on different networks can be joined through a VPN tunnel so the swarm ports never face the internet. Set these when you init the swarm:
- Advertise address = the manager's tunnel IP (e.g.
10.99.0.1). The control port2377then listens on that address only; Listen address overrides it. Without any address it stays on every interface. - Overlay MTU = the tunnel MTU,
1420for WireGuard's default. Docker takes 50 off for the VXLAN header, so containers get1370. Without it the overlay assumes a 1500 network (containers get 1450), and larger packets are dropped inside the tunnel. - Ports
2377/tcp,7946and4789/udponly need to be reachable over the tunnel. On the worker, join with--advertise-addr <tunnel-ip> --listen-addr <tunnel-ip>:2377.
The MTU is fixed when the overlay network is created: to change it on an existing swarm, switch the applications back to container mode, leave the swarm and initialise it again.
Managing nodes
| Action | Effect |
|---|---|
| Availability: active | The node accepts new tasks. |
| Availability: pause | Existing tasks keep running; no new tasks are accepted. |
| Availability: drain | Tasks are moved to other nodes — use before maintenance. |
| Role: manager / worker | Promote or demote a node. Keep the number of managers odd (1, 3, 5) for safe quorum. |
| Label | A key=value pair, e.g. zone=eu — used as a placement constraint. |
| Remove node | Rejected (409) if the node is still active; drain it first, then remove. On that node, run docker swarm leave. |
Applications as a service
In the application's Settings, switch Deploy mode from container to service:
| Field | Notes |
|---|---|
| Replicas | The number of tasks to run. Stop scales to 0; Start restores it. |
| Placement | A specific node, or a free-form constraint (node.labels.zone==eu, node.role==worker, node.hostname, node.platform.os). |
| Rolling update | Parallelism (how many tasks are replaced at once), the delay between batches, and the order (start-first / stop-first). |
- Changing the mode, replicas, placement, or limits runs as a
configdeployment — no build, and it doesn't block requests. - The daemon handles the rolling update itself; if new tasks never come up within the time limit, the old spec is restored and the application keeps serving.
- An application with a mount is always placed on the host where its volume lives — Docker volumes don't move between nodes.
- Redeploying with an identical spec still triggers a rolling update (
ForceUpdate), so the Deploy button always has an effect.
What to know about tasks
| Aspect | In service mode |
|---|---|
| Logs | All tasks, from any node. |
| Metrics | Summed from tasks running on the host — the Engine API can't read tasks on other nodes. |
| Job & terminal exec | Local tasks only. |
| Notifications | A watcher alerts when a service stays below its replica count (tasks that never start), and another reports tasks that crash on other nodes within about 30 seconds. |
| Domain | Traefik's swarm provider routes to the service, not to a container. |
Leaving swarm
docker swarm leave themselves.postgres and goes down. Right after leaving, run this on the server; every aoox container starts again by itself:sudo systemctl restart dockerCommon pitfalls
| Symptom | Cause & fix |
|---|---|
| Task pending: no suitable node | The constraint doesn't match any node's labels, or the app has a mount that pins it to the host. Loosen the placement. |
| On another node: image pull failed / x509 | REGISTRY_PUBLIC_HOST is still localhost, the registry has no TLS, or the node lacks an insecure-registries entry. |
| Service never reaches its replica count | A health check is failing or resources are short; the rolling update auto-rolls-back after a timeout. Check the task logs. |
| Metrics empty for an app on another node | Expected — metrics only come from local tasks. |
| Node missing after reboot | The node's daemon hasn't started, or a firewall is blocking a swarm port (2377/7946/4789). |
Next steps
Found a mistake? Edit this page on GitHub ↗