
Scaling
Docker Swarm vs Kubernetes: Four Gates, Not Node Count
Docker Swarm vs Kubernetes is not a node count. Three capability gates route the choice, a fourth only ever blocks, and Engine 29.9.0 still ships Swarm fixes.
Every comparison of these two opens on the wrong axis: Swarm for small clusters, Kubernetes for serious ones, pick by headcount or ambition. That framing is cheap to write, impossible to falsify, and leaves unexamined control planes in a great many racks — stood up because an article called Kubernetes the standard.
The question is not whether Swarm is dead
Most results get the facts wrong, because two products share the word. Docker's own deprecated-features page lists one: standalone, "classic" Swarm, deprecated in Engine v20.10 and removed in v23.0, and with it overlay networks using an external key/value store. The --cluster-advertise, --cluster-store and --cluster-store-opt daemon options went too, as did --default-stack-orchestrator, because Swarm is now the only — and default — orchestrator for stacks.
Swarm *mode*, built into the engine since 1.12, is a separate thing, not on that page — the release notes are the live evidence. Engine 29.9.0, dated 2026-10-08, fixes Swarm tasks on overlay networks being rejected when the daemon cannot write to /var/lib/docker, and a published port being unreachable from another container on the same network when inter-container communication is disabled — including Swarm services behind the routing mesh. Earlier: 29.5.0 prevented Raft snapshot corruption when swarm state is large, 29.8.0 fixed Swarm service names failing to resolve indefinitely after a missed membership announcement, 29.0.0 dropped known insecure cipher suites from SwarmKit's internal TLS.
Two control planes, and the floor each one puts under you
Each platform asks you to stand up and keep alive things before the first workload runs — the floor, paid whether or not you use what sits above it.
Swarm's floor is the engine you already installed. docker swarm init makes a node the first manager; managers replicate state through Raft, which Docker's admin guide says requires a majority of managers — the quorum — to agree on proposed updates. Overlay networking, the routing mesh, secrets and configs come free. Docker recommends a maximum of seven managers and an odd number, because the next even number does not make the quorum easier to keep; the admin guide adds no hard limit, but says more managers reduce write performance. Workers only execute containers.
Kubernetes asks for etcd, an apiserver, a scheduler, a controller-manager and a kubelet per node — then for decisions. kubeadm's cluster-creation guide is explicit: you must deploy a CNI-based Pod network add-on so Pods can communicate, only one per cluster, and cluster DNS — CoreDNS — will not start up before a network is installed. That gate is kubeadm's, not Kubernetes as such; distributions shipping their own CNI and CoreDNS do not impose it. Either way a CSI driver, an ingress controller, metrics-server and an audit sink stay separate choices with separate lifecycles.
Gate one: does anything here need to scale itself?
Open your Compose file and read every deploy: block. Per service: is the replica count a function of load — something a controller changes unwatched — or a number a human picked and keeps picking?
Swarm honours the second answer only. The Compose deploy reference lists four replication models under mode: global, replicated, replicated-job, global-job. Each is a declared count or per-node placement; none observes a metric. Kubernetes answers the first: a HorizontalPodAutoscaler automatically updates a workload resource with the aim of automatically scaling capacity to match demand, as a control loop running intermittently — default interval 15 seconds. Not free-standing: administrators must enable the API aggregation layer and register the metrics APIs, resource metrics from metrics.k8s.io, generally metrics-server. Event-driven metric sources are another component again: KEDA or another external-metrics adapter. Scale-to-zero is not: HPAScaleToZero has been beta and enabled by default since v1.37, so spec.minReplicas: 0 is accepted once an object or external metric is configured, and rejected otherwise — CPU and memory can only be measured on running Pods.
The gate fires when a replica count genuinely tracks load. In many self-hosted estates it does not: fixed web replicas, one worker per queue, a database never rescheduled — a declared topology Swarm's model does not constrain. Watch the placement constraints; they bite first. A task matching no node is not rejected but waits in Pending, so tier == edge on a swarm where nothing carries that label deploys nothing:
# Swarm honours all of this. Nothing reads a metric.
services:
web:
image: nginx:1.29-alpine
ports:
# ingress mode: routing mesh answers at every node
- "8080:80"
networks:
- edge
deploy:
mode: replicated
replicas: 4
placement:
constraints:
- node.role == worker
- node.labels.tier == edge
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 128M
update_config:
parallelism: 1 # Swarm scheduler default
delay: 20s
order: start-first
failure_action: rollback
monitor: 30s
restart_policy:
condition: on-failure
metrics-agent:
image: prom/node-exporter:v1.9.1
deploy:
mode: global # one per node by definition
networks:
edge:
driver: overlay
driver_opts:
encrypted: "true" # key presence enables IPsec; value unparsed
Gate two: can you show an auditor who did what?
This gate is about evidence, not trust, and its scope matters: it compares *Docker Engine's* daemon access control with Kubernetes; commercial Swarm answers differently, below. Docker's protect-access guidance says anyone with a client's TLS keys can give any instructions to your daemon, giving them root access to the machine hosting the daemon. The documented way to narrow that is authorization plugins — more fine-grained control supplementing mutual-TLS authentication. That is identity at the socket plus whatever a plugin allows, not a per-verb record you can hand someone a year later.
Kubernetes ships that record. Auditing is described as a security-relevant, chronological set of records documenting the sequence of actions in a cluster — users, applications using the API, and the control plane itself. The policy sets depth at four levels: None, Metadata (user, timestamp, resource, verb, no bodies), Request (adds the request body), RequestResponse (both). Be precise: the policy is a configuration input passed to kube-apiserver with --audit-policy-file; omit the flag and no events are logged. The artefact an auditor is handed is the log downstream, written by the log backend as JSON lines — disabled in turn if --audit-log-path is unset.
apiVersion: audit.k8s.io/v1
kind: Policy
# In order; FIRST match decides the level.
omitStages:
- "RequestReceived"
rules:
# Never record secret payloads.
- level: Metadata
resources:
- group: ""
resources: ["secrets", "serviceaccounts/token"]
# Full bodies for authz and workload mutations.
- level: RequestResponse
verbs: ["create", "update", "patch", "delete"]
resources:
- group: "rbac.authorization.k8s.io"
resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]
- group: "apps"
resources: ["deployments", "statefulsets", "daemonsets"]
# Exec/attach are hands-on-production.
- level: Request
resources:
- group: ""
resources: ["pods/exec", "pods/attach"]
# Drop control-plane reads.
- level: None
users: ["system:kube-scheduler", "system:kube-controller-manager"]
verbs: ["get", "list", "watch"]
- level: Metadata
The correction this gate most often needs: commercial Swarm has both. Mirantis's 2025-07-01 announcement states that Swarm users can deploy DISA STIG compliant solutions complete with TLS authentication, image signing, audit logging and RBAC. MKE 3.9's manual documents MKE audit logging and Swarm RBAC grants, and records that workloads can be scheduled by Kubernetes, Swarm, or a combination of the two — though in a mixed cluster the orchestrators are not aware of each other. So a hard audit requirement routes to Kubernetes *or* MKE, never to "Swarm has no equivalent."
Gate three: what does your state actually need?
Swarm does have CSI. Cluster Volumes is described in the moby tree as a new feature which allows using CSI plugins to create cluster-aware volumes, mounted with --mount type=cluster,src=my-volume,dst=/srv/www. Two constraints come with it. The one deciding this gate: the CSI spec allows a large number of features which Cluster Volumes in this initial implementation do not support, most notably snapshots, cloning, or volume expansion. The structural one: they work only with Docker Services.
Read that limit precisely: it describes the moby implementation — the file still calls itself an initial implementation, last changed 2022-08-03 — not every product scheduling Swarm workloads, nor the CSI driver underneath, which may well do snapshots. The question is whether this orchestrator asks it to.
Does a stateful workload use a *volume* snapshot as its backup or rollback mechanism, rather than a logical dump? Does capacity planning assume you can grow a volume in place? Does anything clone a volume to seed staging? If yes, the gate fires — before migration, not during it. If no — PostgreSQL on WAL archiving, grown in a maintenance window — the missing snapshots are a constraint you would never have met.
Gate four: which upgrade floor are you signing?
The first three gates select; this one only brakes — and the figure usually quoted for it is the wrong one. Fourteen months is how long Kubernetes supports one active patch series — twelve standard, then two in maintenance mode — not the interval between upgrades. Releases currently happen approximately three times per year. A team staffing one upgrade a year reads the fourteen-month number, passes this gate, and ends up permanently behind a supported version. So the gate asks not whether you can manage an annual upgrade, but whether you can absorb minors at the project's cadence, indefinitely.
The version-skew policy removes the shortcut: kube-apiserver may not skip minor versions when upgrading, even in single-instance clusters. A kubelet may be up to three minors older and never newer; in HA clusters the newest and oldest kube-apiserver instances must be within one minor. Illustration, not durable fact: the patch-releases page, read 2026-10-10, puts 1.34's end of life at 2026-10-27 and 1.35's at 2027-02-28 — four months apart, so a hop at EOL lands on a minor already well through its own window. Lean on the policies, not the table — the mechanics of that treadmill are their own discipline.
The asymmetry matters. A *no* blocks Kubernetes even where gate one or three selected it, leaving two honest outcomes: run Swarm and live with the gap, or buy the upgrade floor elsewhere — a managed control plane, or MKE. A *yes* fires nothing. If no capability gate fired, the answer is Swarm whatever your upgrade capacity — an unused control plane does not become useful by being maintainable.
Failure modes you will actually meet
Quorum loss is feared and misunderstood. Docker's admin guide is precise: if the swarm loses the quorum of managers, the swarm cannot perform management tasks — but swarm tasks on existing worker nodes continue to run. Serving traffic survives; deploying, scaling and rescheduling do not. Hence a single-manager swarm is OK for testing purposes; surviving a manager failure needs several.
The default secret posture needs the same precision. Swarm secrets are encrypted in transit and at rest, stored in the encrypted Raft log and decrypted into an in-memory filesystem at /run/secrets/<secret_name> in Linux containers — on Windows containers they are persisted in clear text to the container's root disk, though removed when the container stops. The catch: unless autolock is enabled, the mutual TLS key and the Raft log encryption key sit unencrypted on disk. Encrypted at rest, key beside it. Autolock costs an unlock step per manager restart — a deliberate trade.
# First manager. Advertise the address peers reach, then print the token.
docker swarm init --advertise-addr <MANAGER-IP>
docker swarm join-token manager
# On managers 2 and 3 only. Odd counts.
docker swarm join --token <MANAGER-JOIN-TOKEN> <MANAGER-IP>:2377
# Back on manager 1. Participating = Leader + Reachable; quorum is a majority of .Swarm.Managers.
docker node ls --format '{{.Hostname}}\t{{.ManagerStatus}}\t{{.Status}}'
docker info --format 'managers={{.Swarm.Managers}} nodes={{.Swarm.Nodes}}'
# Autolock is OFF by default; until this runs both keys sit on disk.
docker swarm update --autolock=true
# Store off-host. Each restart needs: docker swarm unlock
docker swarm unlock-key --quietCVE-2026-92542 — GitHub: Moderate, CVSS v4 6.9 — is an encrypted-overlay flaw: the firewall rules marking VXLAN datagrams for encryption match both authentic datagrams from the kernel and forged ones from user processes, so an unprivileged user on a Linux Swarm node can inject forged Ethernet frames into a peer node's encrypted overlay. Affected: <= 25.0.18 and 26.0.0 - 29.8.1; the engine fix is v29.8.2, released 2026-09-30. One trap: v25.0.19 appears there as the patched Go module, not an engine release. 29.9.0 fixes errors programming the kernel to encrypt the overlay data-plane that could put encrypted-overlay traffic to some nodes on the wire in cleartext — bounded, since peers drop it as spoofed, though the confidentiality loss is real for unidirectional flows such as UDP DNS queries and TCP SYNs whose handshake never proceeds. And 29.2.1 fixes encrypted overlay networks not passing traffic to containers on v28 and older engines, while the same note adds that encrypted overlay networks will no longer pass traffic to containers on v29.2.0 through v29.0.0, v28.2.2, v25.0.14 or v25.0.13 — a short version denylist to drain, not a reason to big-bang the fleet. The option is keyed rather than parsed — secure when encrypted is present, whatever its value.
#!/usr/bin/env bash
set -euo pipefail
# Affected: <= 25.0.18, 26.0.0 - 29.8.1. Fix: 29.8.2 (2026-09-30).
ver=$(docker version --format '{{.Server.Version}}')
echo "engine: $ver"
# Reports key presence: an absent key prints the name only, while
# --opt encrypted prints encrypted=present value="". An empty value still
# means encryption is on.
docker network ls --filter driver=overlay --quiet |
xargs -r docker network inspect \
--format '{{.Name}}{{range $k, $v := .Options}}{{if eq $k "encrypted"}} encrypted=present value={{printf "%q" $v}}{{end}}{{end}}'
# As published: block USERSPACE processes sending UDP to the Swarm data-path
# port. Remove after upgrading. The
# "-m owner --socket-exists" match limits the DROP to a local socket; without
# it the rule matches the kernel's own VXLAN output and blackholes every
# overlay network on the node. Never truncate it.
# Gate on the running engine: a patched node must not get the rule.
if [ "$(printf '%s\n' "$ver" 29.8.2 | sort -V | head -n1)" = "$ver" ] &&
[ "$ver" != 29.8.2 ]; then
iptables -I OUTPUT -p udp \
--dport "$(docker info --format '{{.Swarm.Cluster.DataPathPort}}')" \
-m owner --socket-exists -j DROP
else
echo "engine $ver is >= 29.8.2: patched, no rule applied"
fiThe exit ramp, in both directions
Optionality means pricing the way out before you need it; the ramps are not symmetrical. Swarm to Kubernetes is well-trodden: Kompose converts Docker Compose to container orchestrators such as Kubernetes, and says conversions are not always 1-1 but will help get you 99% of the way there. What converts is the shape — services, images, ports, environment. What does not is what you spent money on: deploy: constraints become affinity rules, the routing mesh an ingress controller someone operates, overlay encryption a CNI decision, /run/secrets a Secret plus a mount plus an RBAC rule. The image layer is portable; the orchestration layer is a rewrite with a head start.
Kubernetes to Swarm is rarer and has no tool, an honest test of what you were using. If the manifests reduce to Deployments, Services and ConfigMaps, the translation is mechanical. If they involve CRDs, operators, admission webhooks or an HPA that genuinely moves, they do not reduce at all — the cleanest proof a capability gate really fired. The flow shows a third door, dashed: both, on separate clusters — legitimate if it is a decision, not an accident.
The vendor ramp needs the same scepticism. Mirantis committed on 2025-07-01 that Swarm will be fully supported through at least 2030 as part of Mirantis Kubernetes Engine 3. IREN's FY2026 Form 10-K records the acquisition in early August 2026 without settling on a single day: one passage gives August 4, 2026, another August 3, 2026, the date 12.6 million Ordinary shares were issued to former Mirantis holders. Thirteen months later, the vendor behind commercial Swarm support changed hands.
The long game: the floor outlives the workload
So the question is not which orchestrator is more capable — Kubernetes is, uncontroversially — but which floor you can still stand on once the person who built it has gone. Swarm's fits in one head, with a documented commercial horizon at 2028-MAR-24. Kubernetes' is large, deeply documented, hired for everywhere, and asks that upgrade cadence in perpetuity. Different bills, not flaws.
Re-test the gates annually; every input moves. Gate one when a load profile changes, gate two when a contract or regulator arrives, gate three when the moby implementation gains features — gate four on its own, every release. The same discipline applies at the VM layer and at the edge: own the decision record, not the conclusion. Run the smallest control plane that passes your gates, and keep the migration path priced.
Defensible in either direction — unlike "Kubernetes, because that is what people use," the answer wrong at every scale.
§FAQ/Common questions
Frequently asked
Is Docker Swarm still supported in 2026?
Swarm mode is. The confusion comes from two products sharing a name: standalone "classic" Swarm was deprecated in Docker Engine v20.10 and removed in v23.0, per Docker's own deprecated-features page, along with overlay networks using an external key/value store and the --cluster-advertise, --cluster-store and --cluster-store-opt daemon options. Swarm mode — the orchestrator built into the engine — is not on that page, and the engine release notes are the live evidence: Engine 29.9.0, dated 2026-10-08, fixes Swarm tasks on overlay networks being rejected when the daemon cannot write to /var/lib/docker, and a published port being unreachable when inter-container communication is disabled, including Swarm services published through the routing mesh. Commercially, Mirantis stated on 2025-07-01 that Swarm will be fully supported through at least 2030 as part of Mirantis Kubernetes Engine 3, though the furthest documented EOL for a Swarm-capable MKE release is 3.9 at 2028-MAR-24.
At how many nodes should I move from Docker Swarm to Kubernetes?
Node count is the wrong input, and no documented threshold exists. Three capability questions decide it. Does any replica count need to be a function of load? Swarm's deploy.mode offers global, replicated, replicated-job and global-job — all declared counts or per-node placements — while a Kubernetes HorizontalPodAutoscaler scales capacity to match demand. Must you evidence who did what? On plain Docker Engine, a client's TLS keys give root access to the machine hosting the daemon. Do your stateful workloads need snapshots, cloning or volume expansion? The moby documentation states that Cluster Volumes in their initial implementation support none of the three. A fourth question only ever blocks, and its usual framing is wrong: fourteen months is how long Kubernetes supports one minor — twelve months standard, then two in maintenance mode — not the gap between upgrades. Releases happen approximately three times per year, and project policy requires kube-apiserver not to skip minor versions when upgrading, so if you cannot absorb minors at that cadence indefinitely, that rules Kubernetes out whatever the first three said.
How many manager nodes does a Docker Swarm need?
Docker recommends an odd number, on the stated grounds that the next even number does not make the quorum easier to keep, and a maximum of seven managers. That seven is a recommendation rather than an enforced cap: the admin guide says there is no limit on the number of manager nodes, but additional managers reduce write performance because more nodes must acknowledge proposals. Raft requires a majority of managers — the quorum — to agree on proposed updates. A single-manager swarm is documented as OK for testing purposes, and recovering from the failure of a manager without downtime requires multiple managers. Three is not a documented minimum — it follows from those recommendations as the smallest odd count above one that survives a single manager failure. Worker nodes are engines whose sole purpose is to execute containers, so they are cheap to add independently.
What happens to a Docker Swarm if it loses manager quorum?
Serving traffic survives; changing anything does not. Docker's admin guide states that if the swarm loses the quorum of managers, the swarm cannot perform management tasks — and that even then, swarm tasks on existing worker nodes continue to run. So existing containers keep serving, but deploys, scaling, rescheduling and node changes stop until quorum returns. Worth knowing alongside it: tasks whose placement constraints match no available node are not rejected, they wait in Pending until a matching node becomes available. The practical consequence is that quorum loss is an outage of your ability to operate rather than of the service, which is a different severity from the one most people assume.
Does Docker Swarm support CSI storage, RBAC and audit logging?
Partly, and the answer depends on which product. Swarm gained CSI through Cluster Volumes, which the moby documentation describes as allowing CSI plugins to create cluster-aware volumes, mounted with --mount type=cluster; the same document states that in this initial implementation they do not support snapshots, cloning, or volume expansion, and are only compatible with Docker Services, not plain containers. On RBAC and audit, plain Docker Engine offers mutual TLS plus authorization plugins for finer-grained control — not a per-verb audit record. Commercial Swarm is different: Mirantis's 2025-07-01 announcement states Swarm users can deploy DISA STIG compliant solutions complete with TLS authentication, image signing, audit logging and RBAC, and MKE 3.9's manual documents MKE audit logging and Swarm RBAC grants. So a hard audit requirement routes to Kubernetes or to MKE, never to "Swarm has no equivalent."
Further reading
- Kubernetes Upgrade Debt: Skew, Dead APIs, Safe Paths
- KEDA and Descheduler: Two-Tier Autoscaling on Bare Metal
- Docker vs LXC: Application Container or Whole Machine
- Leaving VMware Without Kubernetes: Proxmox VE in Production
- Caddy, Traefik, NGINX, HAProxy: Who Writes Your Edge Config
- Repatriation Cost: A Five-Year TCO Model You Can Defend
Executive Briefing
Thirty minutes to clarify your infrastructure risk
Walk us through your vendor footprint and regulatory constraints. We will tell you honestly where sovereignty creates leverage — and where it does not. No pitch deck. No obligation.