← How-Tos
raspberry-pi 1 hr ago ◯ 6 min read

Docker Swarm on Raspberry Pi: A Lighter Alternative to Kubernetes for Small Clusters

docker swarmraspberry piclusterkubernetesk3sself-hosteddockerhomelaborchestration

Kubernetes via K3s gets most of the attention when a Raspberry Pi cluster build comes up — it's the industry-standard orchestrator, it's what employers want on a resume, and K3s makes it genuinely approachable on a Pi. But K3s is also a lot of machinery for a home cluster that's mostly running a handful of self-hosted services: Pi-hole, a Node-RED instance, maybe Uptime Kuma watching the shop network. Docker Swarm, Docker's own built-in orchestrator, does a smaller job with a fraction of the moving parts, and for a 3-to-6-node Pi cluster running a stack of home services rather than a production-grade microservice architecture, it's often the better fit.

This guide covers setting up a Swarm cluster across a handful of Raspberry Pis, what you get and give up compared to Kubernetes, and a working stack file to get real services running.

Swarm vs. Kubernetes (K3s): What Actually Differs

Docker SwarmK3s (Kubernetes) Install footprintBuilt into Docker Engine, zero extra installSeparate binary/service, ~512MB+ RAM overhead per node Config formatdocker-compose.yml, extended for Swarm (deploy keys)Kubernetes YAML manifests (Deployments, Services, Ingress, etc.) Learning curveLow if you already know docker-composeSteep — pods, services, ingress controllers, RBAC, CRDs Rolling updatesBuilt in, simpleBuilt in, more configurable Load balancingBuilt-in routing mesh, no extra componentNeeds an ingress controller (Traefik ships with K3s) Ecosystem / Helm chartsNone — you write your own compose filesHuge Helm chart ecosystem, most self-hosted apps have one Auto-healing / scalingBasic service replica managementMuch more sophisticated (HPA, probes, pod disruption budgets) Best forSmall home clusters running a known set of containersLarger or more dynamic deployments, learning industry-standard tooling

If your goal is specifically to build production Kubernetes skills, there's no substitute — go read our K3s cluster guide instead. If your goal is a reliable, low-maintenance way to run a handful of containers across several Pis with automatic restart and rolling updates, Swarm gets there with far less YAML and far less RAM overhead reserved just for the orchestrator itself, which matters a lot on 4GB or 8GB Pi nodes where you'd rather that memory went to the actual workloads.

Hardware and OS Prep

Any mix of Raspberry Pi 4 or 5 boards works; they don't need to be identical, though matching OS versions avoids surprises. For each node:

  1. Flash 64-bit Raspberry Pi OS Lite (Swarm/Docker doesn't need a desktop environment, and the 64-bit build matters — some container images don't ship armhf builds anymore).
  2. Set a static IP or DHCP reservation per node, and give each a clear hostname (pi-swarm-1, pi-swarm-2, etc.) during imaging via Raspberry Pi Imager's advanced options.
  3. Install Docker Engine on every node: curl -fsSL https://get.docker.com | sh, then add your user to the docker group and reboot.
  4. Confirm all nodes can reach each other on the required Swarm ports before going further: TCP 2377 (cluster management), TCP/UDP 7946 (node communication), and UDP 4789 (overlay network data). On a flat home network with no extra firewalling between devices this usually just works; if you've segmented your shop network with VLANs, make sure the Pi cluster nodes share a VLAN or that these ports are explicitly routed.

Initializing the Swarm

On your intended manager node:

docker swarm init --advertise-addr <manager-ip>

This prints a docker swarm join command with a worker token. Run that exact command on each of the other Pis to add them as workers. For a cluster of 3 or more nodes, promote at least two more to manager status for quorum and fault tolerance:

docker node promote pi-swarm-2 pi-swarm-3

Swarm's manager quorum uses Raft consensus and needs an odd number of managers to tolerate failures cleanly — 3 managers tolerates 1 failure, 5 tolerates 2. A 2-manager setup gives you no more fault tolerance than a single manager, so for a small cluster either run exactly 1 manager (accepting it as a single point of failure for cluster management, though running services stay up) or go to 3.

Check cluster state from any manager:

docker node ls

A Working Stack File

Swarm deploys from compose files extended with a deploy key. Here's a minimal stack running Pi-hole and Uptime Kuma across the cluster, with placement constraints pinning Pi-hole to a specific node (DNS services need a stable, known IP, which Swarm's routing mesh complicates if the service can land on any node):

version: "3.8" services: pihole: image: pihole/pihole:latest ports: - "53:53/tcp" - "53:53/udp" - "8080:80/tcp" environment: TZ: 'America/New_York' volumes: - pihole-etc:/etc/pihole - pihole-dnsmasq:/etc/dnsmasq.d deploy: placement: constraints: - node.hostname == pi-swarm-1 restart_policy: condition: on-failure uptime-kuma: image: louislam/uptime-kuma:latest ports: - "3001:3001" volumes: - uptime-kuma-data:/app/data deploy: replicas: 1 restart_policy: condition: on-failure volumes: pihole-etc: pihole-dnsmasq: uptime-kuma-data:

Deploy it with:

docker stack deploy -c stack.yml homelab

Swarm's routing mesh means any published port is reachable from any node's IP, not just the one the container landed on — useful for stateless services, but it's exactly why a stateful service like Pi-hole, which needs a predictable address for your router's DHCP to hand out as the DNS server, should be pinned with a placement constraint rather than left to float.

Volumes and Shared Storage

Local Docker volumes (as used above) live on whichever node the container is scheduled to, which is fine for pinned services but breaks if Swarm reschedules a non-pinned container with persistent data to a different node after a failure. For services where that matters, point the volume at an NFS share hosted on one Pi (or a NAS) instead of a local Docker volume — our redundant Pi NAS guide covers building that share. A straightforward NFS volume driver config looks like:

volumes: shared-data: driver: local driver_opts: type: nfs o: addr=192.168.1.50,rw device: ":/mnt/nfs/docker-data"

Monitoring and Day-2 Operations

Swarm's own tooling is thin by design: docker service ls and docker service ps <name> show what's running and where, docker service logs <name> tails logs across every replica of a service. For anything beyond that, deploy a Prometheus/Grafana stack as another Swarm service pointed at cAdvisor on each node, which gives per-container CPU, memory, and network graphs without installing anything outside the cluster itself.

When to Actually Pick K3s Instead

Swarm's limits show up once a deployment grows past "small home stack": there's no native support for horizontal pod autoscaling, no readiness/liveness probe ecosystem as mature as Kubernetes', and most of the self-hosted app community publishes Helm charts, not Swarm stack files, meaning you'll be hand-translating configs more often than not. If you're chasing a specific self-hosted app that only ships a Helm chart, or you want the orchestration skills to transfer directly to a job, K3s is worth the extra complexity. For a cluster whose entire job is keeping Pi-hole, a dashboard, and a couple of automation tools running reliably across a few Pis sitting on a shop shelf, Swarm gets there in an afternoon with a fraction of the YAML.