If you already run Docker Compose on a single Proxmox LXC or
bare-metal Linux host, you have a solid container setup. But Compose
leaves gaps: containers run until they crash, there is no built-in
health-based reconciliation, and rolling out a new version means
manual docker compose up -d with a brief window of downtime.
Docker Swarm fills those gaps with zero additional dependencies.
It is built into Docker Engine and turns your single host into a
one-node cluster with declarative reconciliation, rolling updates,
automatic restart on failure, and built-in service discovery. Best
of all, you keep the same compose file format — just add a deploy
section and run docker stack deploy instead of docker compose up.
This guide walks through initialising a single-node Swarm, converting Compose files to stacks, deploying Traefik and Portainer as Swarm services, managing persistent storage, and the day 2 operations you need for a production homelab.
Why Docker Swarm for a Single-Node Homelab
The common objection: “Swarm is for multi-node clusters. I have one machine.” The answer: Swarm’s orchestration engine gives you infrastructure-level reliability on that one machine that Compose simply does not provide.
| Capability | Docker Compose | Docker Swarm |
|---|---|---|
| Desired-state reconciliation | Manual up/restart | Automatic |
| Failure recovery | Stays stopped | Restarts with backoff |
| Rolling updates | Manual | Declarative, configurable |
| Rollback on failure | Manual | Automatic on failure detection |
| Service scaling | Scale with up | deploy.replicas |
| Built-in DNS/service discovery | No | Per-service A records |
For a homelab running critical services — DNS, reverse proxy, Git server, monitoring — Swarm’s automatic reconciliation is the difference between “woke up and found Immich was down since 3 AM” and waking up to a notification that the service restarted and is healthy again.
Prerequisites for Docker Swarm
- Docker Engine 24+ — Swarm mode is shipped with the engine, no extra package
- Docker Compose v2 (optional, for local testing before deploying as stack)
- NFS server or adequate local disk for persistent volumes
- Port 2377/tcp open (Swarm manager communication) — only needed if you ever add nodes
- Ports 7946/tcp+udp and 4789/udp for overlay networking (safe to leave closed on single-node, but opened for future nodes)
Verify your Docker Engine supports swarm mode:
|
|
Output on a Swarm-enabled host (even if not in Swarm mode):
Swarm: inactive
Initialise the Swarm on Your Homelab Host
Pull the trigger:
|
|
Replace 10.0.20.30 with your homelab server’s LAN IP. On a
single-node setup, this IP only matters for the manager election —
no other nodes need to reach it unless you expand later.
Output:
Swarm initialized: current node (abc123def456) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-xxxxx 10.0.20.30:2377
To add a manager, run 'docker swarm join-token manager' and follow the instructions.
Verify the cluster state:
|
|
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
abc123def456 * srv1 Ready Active Reachable 27.4.1
On a single-node homelab, the manager also runs workloads. No need to drain the manager — you are not adding worker nodes.
Deploy Your First Swarm Service
A quick smoke test to confirm Swarm is working:
|
|
Then scale it to two replicas:
|
|
Access http://10.0.20.30:8080 and refresh — each request hits a
different replica container. Swarm’s internal load balancer round-robins
across them.
Clean up:
|
|
Convert a Docker Compose File to a Swarm Stack
This is the core migration: turning your existing docker-compose.yml
into a docker-stack.yml that docker stack deploy can consume.
Key Differences Between Compose and Stack
1. Version field is optional. Modern Docker (24+) accepts stack
files without version:. Omit it.
2. Use deploy: instead of direct resource fields.
|
|
3. Networks must exist or be declared as overlay.
|
|
attachable: true allows standalone containers (e.g., debugging tools)
to connect to the overlay network.
4. Volumes work the same way for single-node.
Named volumes like postgres_data: resolve to the local Docker
volume store. For multi-node, you would use NFS, but on a single node,
local bind mounts and named volumes are fine.
Full Example: Static Site with Traefik
Here is a complete docker-stack.yml that deploys an nginx static
site behind Traefik:
|
|
Deploy with:
|
|
Note the traefik.http.services.<name>.loadbalancer.server.port label.
In Swarm mode, Traefik needs an explicit port declaration — it cannot
inspect container ports the same way it does on Compose.
Deploy Traefik as a Swarm Service
Traefik must run in Swarm mode to work with stack-deployed services. Here is the Traefik stack definition:
|
|
Key points:
mode: hostfor ports 80 and 443 — bypasses Swarm’s routing mesh for direct host-level port binding (required for Let’s Encrypt HTTP-01 challenges).node.role == managerconstraint ensures Traefik stays on the manager node.traefik.http.services.dashboard.service=api@internalexposes the Traefik dashboard at the/apiendpoint.- The network
traefik-publicmust beexternal: truebecause it was created before the stack.
Create the network and deploy:
|
|
Deploy Portainer for Visual Swarm Management
Portainer gives you a web UI for managing your Swarm services without reaching for the terminal every time. On a single-node setup, deploy both the Portainer Agent and Server:
|
|
Deploy:
|
|
Access Portainer at https://portainer.gntech.dev — you can inspect
running services, view logs, scale replicas, and deploy new stacks
from the web UI.
Persistent Storage in Single-Node Swarm
Named volumes and bind mounts work identically to Docker Compose on a single host. If you ever plan to expand to multiple nodes, move persistent data to an NFS share.
Create an NFS-backed volume:
|
|
Use it in a stack:
|
|
The volume is created externally (with docker volume create) so it
survives stack removal. The stack references it with external: true.
Monitoring the Swarm
Deploy a monitoring stack to keep visibility into your Swarm services:
|
|
Day 2 Operations for Your Swarm Homelab
Rolling update:
|
|
Rollback if something goes wrong:
|
|
View logs across all replicas:
|
|
Inspect service state:
|
|
Scale a service:
|
|
Clean up a stack entirely:
|
|
Drain the node for maintenance (only relevant in multi-node, but noted for completeness):
|
|
Why This Beats Multi-Node Overkill
A single-node Docker Swarm gives you 90% of Kubernetes-style orchestration benefits with zero networking complexity:
- Declarative service definitions that self-heal
- Rolling updates with automatic rollback
- Built-in service discovery via DNS
- Traefik integration with zero extra service mesh
- Portainer for visual management
If you ever outgrow one host, adding a second is as simple as
running docker swarm join on it. Your stacks and networks
stay the same — just add more replicas.
For every Compose service you care about, try the Swarm stack alternative. The file diff is small, the operational improvement is immediate, and you keep the same Docker skills you already have.
Related reading: Docker Compose patterns for homelab, Docker Trivy vulnerability scanning, Traefik middleware security hardening.