Homelab Journey Part 3: Why I Chose Docker Swarm
I chose Docker Swarm because I wanted to run services, not build a platform.
That sounds obvious, but it is easy to forget when you are building a homelab. The interesting option is rarely the quiet one. Kubernetes has more books, more talks, more tooling, and more career gravity. It feels like the serious answer.
For my setup, it was not the right answer.
I have a small Raspberry Pi cluster running services I actually use: Home Assistant, Immich, Gitea, Paperless-ngx, Traefik, Portainer, and a few supporting pieces. The goal is not to recreate a cloud platform at home. The goal is to keep useful services running on low-power hardware with enough automation that I do not dread touching it later.
Docker Swarm sits in the boring middle. That is why it works.
Compose was too small
Docker Compose is still the best starting point for most self-hosted services. One file, one host, easy to understand. If this was all running on a single machine, I would probably stop there.
The problem is that I am not running one machine. I have multiple Pis, and I wanted a clean way to spread services across them without managing every container by hand. I also wanted basic service discovery, simple rollouts, and a deployment model that did not depend on me remembering which node ran what.
Compose can do a lot, but it does not really manage a cluster. Once I started thinking about node failures, placement, and shared networks, I needed something a bit more than docker compose up.
Not a lot more. Just enough more.
Kubernetes was too much
Kubernetes would have solved the orchestration problem. It would also have introduced a pile of new problems that were not worth it for this homelab.
I would have needed to care about cluster components, ingress controllers, storage classes, manifests, operators, upgrades, and a much bigger debugging surface. Some of that is useful knowledge. Some of it is just extra work when the services are small and the hardware is limited.
The Raspberry Pis are already the constraint. Storage is already a compromise. Immich and Paperless-ngx already remind me that low-power hardware has limits. Adding a heavier control plane on top would not make the lab more reliable by itself. It would mostly give me more things to maintain.
That is the trap with Kubernetes at home. It can be the right tool, but it can also become the hobby instead of the thing supporting the hobby.
What Swarm gives me
Swarm gives me the pieces I need without much ceremony.
I can deploy stacks with files that still feel close to Compose. I can use overlay networks so services can talk to each other across nodes. I can add placement constraints when a service needs to run somewhere specific. I can let Swarm restart containers when they fail. I can scale simple stateless services when that makes sense.
Most importantly, I can understand it after a few weeks away.
That matters more than it sounds. Homelab work happens in gaps. A quiet evening. A weekend morning. A spare hour after something breaks. If the system takes half the session to mentally reload, it is too complicated for how I use it.
Swarm keeps the operating model small:
- write or update the stack file
- run the Ansible playbook
- let Swarm place the services
- check the logs
- move on
That is about the right level of machinery for this setup.
The storage reality
Swarm does not magically solve storage. This is the part people skip over when talking about orchestration.
My state lives on shared storage over NFS from the Pi with the 2 TB drive. That keeps service data off SD cards and makes containers easier to move, but it is not high availability. If the storage node has a problem, the cluster has a problem.
That is not Swarm’s fault. It is just the shape of the system.
The useful thing is that Swarm makes compute a bit more flexible while leaving storage simple enough that I can reason about it. I would rather have a known weak point with good backups than a complicated storage layer I do not fully trust.
Where Swarm falls short
Swarm is not perfect.
The ecosystem is much smaller than Kubernetes. Some tutorials are old. Some projects assume Compose or Kubernetes and leave Swarm as an exercise for the reader. You do not get the same depth of operators, controllers, dashboards, or managed patterns.
It also does not remove the need to think. Services with hardware dependencies still need placement rules. Anything using USB devices, local paths, or heavy storage needs care. ARM images can still surprise you. Rolling updates are only as safe as the application and volumes underneath them.
That is fine. I am not looking for magic. I am looking for a tool whose failure modes I can understand.
How I use it
I treat Swarm as a simple scheduler, not a full platform.
Traefik handles routing. Portainer gives me a quick view when I want one. Ansible handles setup and deployment so the process is repeatable. NFS holds the state. Restic handles backups. Swarm ties the running containers together across the Pis.
That separation keeps the design honest. Swarm is not responsible for everything. It does orchestration well enough, and the rest of the system is made of tools I can inspect, replace, or fix individually.
The services that need special treatment get it. Home Assistant is not the same kind of workload as Gitea. Immich is not the same kind of workload as Traefik. Pretending they are all identical just because they run in containers would be lazy engineering.
Why it is good enough
Good enough is not an insult. For a homelab, good enough is often the best target.
Docker Swarm gives me multi-node deployments, service discovery, simple rollouts, and enough resilience for small home services. It lets me learn useful orchestration lessons without turning every change into platform work.
If the lab outgrows it, I can move. That would be a good problem to have. It would mean the requirements changed.
Right now, Swarm matches the shape of the problem. The Pis are useful but limited. The services matter more than the platform. The system needs to be understandable after a gap.
That is enough.