Skip to main content
RunBook Academy

Docker & ContainersXXIII Β· High AvailabilityStandalone limits

Standalone Docker limits β€” when one host is not enough

Intermediate⏱ ~24 mindocker

What you'll learn

  • State precisely what a restart policy does and what it cannot do
  • Distinguish process-level recovery from host-level failover
  • Identify which of four different limits your workload has actually hit
  • Cost each step past standalone Docker honestly, including what it adds

Prerequisites

Verified against Docker Engine 29.x Β· Docker Engine 28.x Β· Docker Compose 2.x Β· containerd 2.x Β· runc 1.2.x Β· BuildKit 0.20+ Β· Linux kernel 5.15+ Β· Ubuntu 24.04 LTS Β· Debian 12 (Bookworm) Β· 2026-08-12

Not yet marked complete on this device.

Say it plainly, because the rest of this part depends on it:

Standalone Docker has no failover. There is no mechanism in the Docker Engine that notices a host has stopped and starts the workload somewhere else. No configuration enables one. restart: always is not it, health checks are not it, and Compose is not it.

This gets believed because the half of it that is true is genuinely useful, and because on the day it is tested nobody is in a position to take notes.

What a restart policy actually does

A restart policy is an instruction to the Docker daemon on one host about what to do when a container on that host exits. It is evaluated by a process that is running. If the daemon is not running β€” because the kernel panicked, the power failed, the disk died or the hypervisor lost the instance β€” there is nothing to evaluate the policy.

PolicyOn container exitOn daemon restartOn host death
no (default)NothingNothingNothing
on-failure[:N]Restarts on non-zero exit, up to N timesDoes not restart itNothing
alwaysRestarts, alwaysRestarts it, even if manually stoppedNothing
unless-stoppedRestarts, unless manually stoppedRestarts, unless manually stoppedNothing

The final column is the entire point of this lesson. It is the same for every policy, because the policy lives on the host that is gone.

Proving it to yourself

Two experiments, five minutes, on a disposable host or VM.

Service impact possiblewhat the policy does cover
docker run -d --name ha-demo --restart always alpine:3.20 sleep 3600

docker kill ha-demo
sleep 3
docker ps --filter name=ha-demo --format '{{.Names}} {{.Status}}'
docker inspect --format '{{.RestartCount}}' ha-demo
Read-only / Safeprocess recovery works
$ docker ps --filter name=ha-demo --format '{{.Names}} {{.Status}}'
ha-demo Up 2 seconds
1

Illustrative output

Service impact possiblewhat it does not cover
# Stop the daemon and its socket, leaving the container process orphaned.
# This approximates a host that stopped answering.
sudo systemctl stop docker.socket docker.service

# From ANOTHER machine, the service is simply gone. Nothing anywhere
# is evaluating a restart policy, because nothing is running.
# curl --max-time 5 http://192.0.2.10:8080/healthz  ->  connection refused

# Restore
sudo systemctl start docker.service docker.socket

The second experiment is deliberately anticlimactic. Nothing happens. That is the finding β€” there is no event, no log line elsewhere, no other host that notices. Whatever noticed in your imagination was an orchestrator you do not have.

The four different ceilings

β€œWe have outgrown one host” is four distinct problems that want four different answers. Diagnosing which one you have is most of the work, because the standard reflex β€” adopt an orchestrator β€” solves the second one and is a poor answer to the others.

Ceiling 1: capacity

The workload no longer fits. CPU is saturated, memory is exhausted, disk I/O is the bottleneck.

This is not an HA problem and an orchestrator does not solve it. Kubernetes on one node has the same capacity as Docker on one node. The answers are a bigger host (which goes further than people expect β€” a modern machine holds a great deal), moving the database off to a managed service, or genuine horizontal scaling. Reach for horizontal scaling because vertical ran out, not because horizontal sounds more serious.

Ceiling 2: host failure must not cause an outage

The actual HA problem, and the only one on this list that standalone Docker cannot address at all. It needs a second host and something that moves traffic. That is the subject of the next lesson.

Ceiling 3: deploys cause downtime

docker compose up on a changed service stops the existing container and creates a replacement. The documentation is explicit: when a service’s configuration or image has changed, Compose β€œpicks up the changes by stopping and recreating the containers”. There is no built-in rolling update and no overlap between old and new.

Note what this is not: the old container is not kept running while the new one warms up. The window is real, and its length is the container’s startup time plus its health check start_period β€” commonly ten to sixty seconds, and much longer for anything that runs migrations at boot.

This is solvable without an orchestrator. Run two instances behind a proxy and recreate them one at a time, draining each first β€” the next lesson’s material. It is manual, it works, and for a deploy or two a week it is entirely reasonable.

Ceiling 4: operational complexity

The Compose file is a thousand lines, deploys are a bash script nobody understands, and three people have to be awake to release. This is the ceiling that most honestly justifies an orchestrator, and it is the one least often cited β€” because it is about the team rather than the technology.

The steps past standalone, and what each costs

StepAddsCostsFails at
One host, restart + health checksProcess recoveryNothingHost death
Two hosts + external LB, manual failoverSurvives host deathA second host, shared state, manual cutoverRequires a human, at night
Two hosts + LB with health checksAutomatic traffic removalShared state must be genuinely sharedSessions and stateful services
Docker SwarmMulti-host scheduling, rolling updates, real failoverA control plane, an overlay network, a smaller communityAdvanced workloads
KubernetesEverythingA large, permanent operational surfaceSmall teams

The second row is the one most single-host deployments should reach for, and it is under-adopted because it is unglamorous. Two Compose hosts and a proxy in front is a genuine availability improvement, it is understandable by one person, and it is a fraction of the operational cost of an orchestrator.

Swarm deserves an honest note. It is still in the engine, it does what it says, and for multi-host scheduling with rolling updates it is dramatically simpler than Kubernetes. Its problem is ecosystem: fewer people know it, fewer tools target it, and a search for a problem you hit at 03:00 returns fewer useful results. That is a real cost and it is the reason most teams skip it, not a technical deficiency.

Knowledge check

Knowledge check Β· 6 questions

  1. Q1. A container runs with `restart: always`. The host suffers a kernel panic. What happens to the container?

  2. Q2. Which restart policy does NOT restart a container after the Docker daemon itself restarts?

  3. Q3. What does `docker compose up` do to a service whose image has changed?

  4. Q4. Which situations does a restart policy genuinely cover? Select all that apply.

  5. Q5. With Postgres running on only one of two application hosts, losing that host leaves a load balancer routing all traffic to a survivor that is healthy by its own check and unable to serve a request.

  6. Q6. A team is at 30% CPU on one host, has had no host failures in two years, deploys once a fortnight at 03:00, and needs three people awake to release. Which ceiling have they hit?

Passing score: 75%. Answers are checked in this browser.