Skip to main content
RunBook Academy

Docker & ContainersXXII Β· Disaster RecoveryState inventory

What is actually stateful β€” and why /var/lib/docker is not the answer

Advanced⏱ ~24 min

What you'll learn

  • Sort everything on a Docker host into recreatable and irreplaceable
  • Find anonymous volumes holding data nothing references by name
  • Explain the four reasons a /var/lib/docker tar is not a usable backup
  • Produce a state inventory a rebuild can be driven from

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-11

Not yet marked complete on this device.

Disaster recovery starts with an inventory, not with a backup tool. The question is narrow and answerable:

If this host disappeared right now, what could not be recreated from a registry, a git repository and a configuration management run?

Whatever is on that list is your entire DR problem. Everything else is a rebuild.

Sorting the host

Containers are not on the list. A container is a process plus a disposable writable layer; you recreate it by running the image again. Images are not on the list either, provided they are in a registry you also protect.

ThingRecreatable fromOn the list?
Containersdocker compose up -dNo
ImagesRegistry, or a rebuild from the DockerfileNo
Build cacheNothing β€” and nothing needs itNo
Compose files, DockerfilesGitNo
daemon.json, systemd drop-insConfiguration managementNo
Named volume contentsNothingYes
Anonymous volume contentsNothingYes
Bind mount source directoriesNothingYes
Secrets and TLS private keysA secret manager, if you have oneYes
Self-hosted registry storageNothingYes
Anything written into a container and not into a volumeNothingYes, and it should not exist

That last row is the one that turns a clean rebuild into a data loss event. A container’s writable layer is discarded when the container is removed β€” which docker compose up does routinely, on every image change. If an application writes state there, the state was already living on borrowed time before any disaster.

Anonymous volumes: state with no name

An image with a VOLUME instruction creates a volume whether or not you asked for one. If nothing is mounted at that path, Docker creates an anonymous volume with a 64-character hex name.

Read-only / Safea real host
$ docker volume ls -q | wc -l
docker volume ls -q | grep -Ec '^[0-9a-f]{64}$'
docker volume ls -qf dangling=true | wc -l
47
37
39

Read those three numbers together. Most of the volumes on this host have names nobody chose, and most are referenced by nothing. Some of them are empty scratch space from an image’s VOLUME declaration. Some of them are the only surviving copy of something.

There is no command that tells you which is which. You have to look.

Read-only / Safetriage the orphans
for v in $(docker volume ls -qf dangling=true); do
mp=$(docker volume inspect "$v" --format '{{.Mountpoint}}')
printf '%s  %s\n' "$(sudo du -sh "$mp" 2>/dev/null | cut -f1)" "$v"
done | sort -h | tail -20

Why a /var/lib/docker tar is not a backup

It is the obvious idea β€” everything Docker owns is under one directory, so copy the directory. It fails for four independent reasons, any one of which is sufficient.

1. It is a live database

/var/lib/docker holds the daemon’s metadata: the image store, containerd’s bolt database, network state, container configuration. The daemon holds these open and writes to them continuously. A tar of a running daemon’s directory is a crash-consistent copy of a database mid-write.

It might restore. Nobody can tell you in advance whether it will, and you find out during the outage.

2. It is mostly things you do not need

On the host above:

Read-only / Safewhat is in there
$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          34        10        17.98GB   14.46GB (80%)
Containers      10        9         192.5kB   49.15kB (25%)
Local Volumes   47        8         2.901GB   1.684GB (58%)
Build Cache     87        0         7.446GB   5.124GB

You would be copying roughly 25 GB of image layers and build cache β€” all of it reconstructible from a registry or a rebuild β€” in order to protect 2.9 GB that matters. Every night, over your backup link, with the retention cost multiplied by every restore point.

3. It is not portable

The layout is an implementation detail of the storage driver, and the storage driver changes. This host reports:

Read-only / Safedriver check
$ docker info --format 'driver={{.Driver}} version={{.ServerVersion}}'
driver=overlayfs version=29.7.1

A /var/lib/docker captured from a 28.x host with the overlay2 graph driver, restored onto a 29.x host using the containerd snapshotter, is not a supported operation. Neither is restoring across a filesystem change, or onto a host whose kernel does not support the same overlay features. Your DR host is very often not identical to the host you lost β€” that is usually why you are rebuilding.

4. It leaks secrets in a form your backup system does not expect

Container configuration under /var/lib/docker includes the environment variables each container was started with. If any service takes a password through the environment, that password is now in every restore point of a filesystem-level backup, in plain text, under a retention policy written for application data.

The inventory as an artefact

The output of this lesson is a file in the repository, not a mental model. Generate it, commit it, and diff it on a schedule so a new unbacked-up volume shows up as a change:

Read-only / Safegenerate the state inventory
docker ps -a --format '{{.Names}}' | sort | while read -r c; do
docker inspect "$c" --format \
  '{{range .Mounts}}{{$.Name}} {{.Type}} {{.Name}}{{.Source}} -> {{.Destination}} rw={{.RW}}
{{end}}'
done

A row with Type=volume and a 64-hex name is an anonymous volume that should be given a name. A row with Type=bind names a host path that your host backup must cover. A service with no rows at all either genuinely holds no state, or is writing to its container layer.

Sanity check

Knowledge check Β· 4 questions

  1. Q1. A host has 47 volumes, 39 of them dangling, and the stack has been stopped for maintenance. What does dangling mean here?

  2. Q2. Which of these is the strongest single reason not to back up /var/lib/docker with tar?

  3. Q3. Which items belong on the irreplaceable list for a Docker host? Select all that apply.

  4. Q4. A service with no volumes and no bind mounts is necessarily stateless.

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