Chart titled Docker Swarm readiness: 10 of 74 containers can float, 64 are tied to one host. A breakdown bar chart shows why they are pinned: local state 53, retired 4, Docker socket 3, host config file 2, fixed address 2.

Can 74 containers float? A Docker Swarm readiness audit

It is tempting to assume these containers are mostly disposable — that they could float across a Docker Swarm and give us high availability for free. I went and looked instead of assuming. The assumption turns out to be mostly wrong, but the ten that can float are genuinely interchangeable, and the reason the other sixty-four cannot is more interesting than a yes or no.

I inspected every container on both Docker hosts in the estate: 63 on dockerlab01 and 11 on the DigitalOcean droplet that serves oddbyte.us. The application host runs no containers of its own. Verdicts come from each container’s live configuration — mounts, networks, devices, published ports and restart policy — not from what an image is called.

Chart titled Docker Swarm readiness: 10 of 74 containers can float, 64 are tied to one host. A breakdown bar chart shows why they are pinned: local state 53, retired 4, Docker socket 3, host config file 2, fixed address 2.
Swarm readiness across 74 containers, from a read-only inspection of both Docker hosts. Chart by Simon.

The answer in one line

10 of 74 containers can be scheduled on any node today. The other 64 are pinned to a particular host — overwhelmingly because they keep their state on a local bind mount or named volume, not because anything is architecturally impossible.

can float      10
tied to host   64
------------------
total          74

How I judged “swarm-able”

A container is ticked as swarm-able only when all of these hold. Anything else gets an open box and a stated reason, so the verdict is checkable rather than a vibe.

  • It keeps no unique state on the host — no data bind mount, no named volume that a replacement task would have to inherit.
  • It needs no Docker socket and no host device, so it is not managing the node it happens to run on.
  • It is not a single-writer database, where running two copies corrupts things instead of scaling them.
  • Nothing depends on its own fixed LAN address. Every container also receives a static address on the internal service network, and that addressing is node-local; anything other machines reach by that address would break the moment the task moved.

Can float — 10 containers

These are the genuinely interchangeable ones. Any of them could be recreated on another node with no data loss and no change to whatever talks to it. Several are small utility services, so availability was never really their problem — they are portable, not necessarily worth replicating.

  • ☑ adventurelog-frontend — no volumes, no host devices
  • ☑ homelable-frontend-1 — no volumes
  • ☑ homelable-mcp-1 — no volumes
  • ☑ it-tools — no state at all
  • ☑ legacy-netalertx — no persistent mounts, though it still needs LAN reachability
  • ☑ native-subagent-docker-trial — throwaway whoami test container
  • ☑ paperless-gotenberg-1 — pure document converter, no volumes
  • ☑ paperless-tika-1 — pure text extractor, no volumes
  • ☑ pricebuddy-scraper — drives the application over the network, keeps nothing local
  • ☑ repo-redis-cache-1 — cache volume is regenerable, so it is stateless in effect

Tied to one host — 64 containers

Grouped by what actually holds them in place. The count in each heading is the size of that group, not a judgement about how hard it would be to fix. Twenty-six of the sixty-four are databases or content stores that would still need somewhere permanent to live after any migration.

Held by local state — 53

Each of these keeps unique data in a bind mount or named volume on whichever node runs it. Recreating the container elsewhere would either lose that data or serve a stale copy. The eleven on the public droplet are included here: every container there is a database or a web application holding its content in a volume. Databases are marked as single writers because they must never run as two replicas at once.

  • ☐ adventurelog-backend
  • ☐ adventurelog-db — single writer
  • ☐ app — Discourse shared and standalone trees
  • ☐ authentik-postgresql-1 — single writer, currently stopped
  • ☐ authentik-server-1 — currently stopped
  • ☐ backrest — mounts eleven other applications’ data plus the backup target
  • ☐ donetick
  • ☐ flightlog-jetlog-1
  • ☐ forum — Discourse shared and standalone trees
  • ☐ homarr
  • ☐ homebox
  • ☐ homelable-backend-1
  • ☐ legacy-heimdall-primary
  • ☐ legacy-heimdall-secondary
  • ☐ legacy-organizr
  • ☐ legacy-prowlarr
  • ☐ maintainerr
  • ☐ mealie — data volume plus secret binds
  • ☐ n8n-oddbyte-n8n-1
  • ☐ n8n-oddbyte-postgres-1 — single writer
  • ☐ n8n-oddbyte-qdrant-1 — single writer
  • ☐ ntfy
  • ☐ nzbhydra
  • ☐ paper-ledger-app-1
  • ☐ paperclip
  • ☐ paperless — media, scans and database binds
  • ☐ paperless-broker-1
  • ☐ paperless-db-1 — single writer
  • ☐ pricebuddy
  • ☐ pricebuddy-database — single writer
  • ☐ radarr — config plus media binds
  • ☐ repo-netbox-1 — configuration plus media, report and script volumes
  • ☐ repo-netbox-worker-1 — shares the application’s volumes
  • ☐ repo-postgres-1 — single writer
  • ☐ repo-redis-1 — task queue state
  • ☐ seerr
  • ☐ sonarr — config plus media binds
  • ☐ tautulli
  • ☐ uptime-kuma — SQLite bind
  • ☐ uptime-kuma-hierarchy-exporter — reads the monitoring database directly
  • ☐ vaultwarden-backup — binds the live vault data
  • ☐ vikunja
  • ☐ Caddy (digilan-caddy-cloudflare) — public droplet, data and config volumes
  • ☐ Wiki.js — public droplet, wiki content volume
  • ☐ Wiki.js PostgreSQL — public droplet, single writer
  • ☐ Stupid Little Cups production — public droplet, SQLite database and uploads
  • ☐ Stupid Little Cups staging — public droplet, separate database and uploads
  • ☐ Lindahl WordPress — public droplet
  • ☐ Lindahl MariaDB — public droplet, single writer
  • ☐ OddByte WordPress — public droplet
  • ☐ OddByte MariaDB — public droplet, single writer
  • ☐ Sugar Cove WordPress — public droplet
  • ☐ Sugar Cove MariaDB — public droplet, single writer

Held by the Docker socket — 3

These talk to the Docker daemon to do their job, so they are meant to be per-node. That is a design decision to declare rather than a limitation to fix.

  • ☐ authentik-worker-1 — Docker socket plus bind-mounted state, currently stopped
  • ☐ portainer — Docker socket plus its own data volume
  • ☐ watchtower — Docker socket; it is a per-node agent by nature

Held by a host config file — 2

These are otherwise portable, but their configuration is supplied from a file on the host rather than baked into the image.

  • ☐ paper-ledger-proxy-1 — reverse proxy whose Caddyfile comes from a host bind
  • ☐ searxng — settings supplied by a host bind

Held by its own fixed address — 2

Both of these are stateless in themselves — and both are unreachable the moment they move, because other machines are configured to point at them.

  • ☐ kms — licence clients are pinned to its address
  • ☐ pihole — the LAN resolves DNS through its address

Retired, but still present — 4

Stopped or dead leftovers kept as rollback material. These should be excluded from any migration plan rather than migrated.

  • ☐ flightlog2_jetlog.1.o2uyz0v95ko10taw6ha15yqns — dead remnant of an earlier Swarm service, which is a useful hint that this host has been through a Swarm before
  • ☐ quirky_jackson — stopped Discourse base container
  • ☐ stupid-little-cups — retired here; the public copy runs on the droplet
  • ☐ vaultwarden — stopped, superseded

What would actually change the answer

  • Shared storage. Most of the state group is not state that has to travel with a container — it is state that has to be visible from whichever node runs it. Put configuration and data directories on shared storage and a large part of that group becomes schedulable immediately. This is the single highest-leverage change.
  • Externalise the databases. The single-writer services should stay single, but they do not have to live on the node that uses them. A dedicated database host frees their applications to float.
  • Separate node agents from workloads. Watchtower, Portainer and the Authentik worker are meant to be per-node. Declare them node-local and stop counting them as migration candidates.
  • Keep fixed addresses for infrastructure services. DNS and the licence server are stateless but deliberately addressed by other machines. That is a decision to make on purpose, not something to schedule away.
  • Leave the retired four alone. They are rollback material, not workloads.

What this audit does not say

  • It is a read-only inspection. Nothing was deployed, and no Swarm was stood up to test these verdicts in practice.
  • “Can float” means it can be scheduled elsewhere — not that it should be. Several of the ten are trivial services where availability was never the problem.
  • Portability to ARM is a separate question from schedulability, and I have not re-run that analysis here. A container can be perfectly swarm-able and still have no ARM build.
  • The droplet figures come from the documented inventory rather than a live enumeration, because I hold no SSH credential for that host. The dockerlab01 figures are from the live host. A one-node Swarm is also not high availability: quorum needs three managers.

The honest headline is that “most of these are stateless” turns out not to be true — 14% are. But the reason is fixable, and the fix is storage architecture rather than a different orchestrator.

Similar Posts