Skip to main content
RunBook Academy

Docker & ContainersXXIX Β· Docker UpgradesExecution

Rollback β€” putting Docker back the way it was

Advanced⏱ ~24 mindocker

What you'll learn

  • Downgrade Docker packages to a specific prior version
  • Pin versions with `apt-mark hold` so the rollback survives the next patch run
  • Identify the state that does not roll back with the packages
  • Choose between package downgrade, snapshot restore, and host rebuild

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.

The upgrade went in and something is wrong. Before reaching for a rollback, be clear about what a rollback actually restores, because β€œput it back” is not one operation and the cheapest version of it does not restore everything.

Three strategies, in increasing order of cost and completeness:

  1. Package downgrade. Reinstall the previous versions. Fast, and restores the binaries and nothing else.
  2. Snapshot restore. Roll the filesystem back to the pre-upgrade state. Restores everything on that filesystem, loses anything written since.
  3. Rebuild from capture. Provision a host at the old version and replay the configuration capture. Slowest, most reliable, and the only one that works when the host itself is the problem.

Package downgrade

Service impact possibledowngrade
#!/usr/bin/env bash
set -euo pipefail

PREV='5:28.1.3-1~ubuntu.24.04~noble'

# Confirm the old version is still in the repository
apt-cache madison docker-ce | grep -F "$PREV" || { echo "previous version no longer published" >&2; exit 1; }

sudo apt-get install -y --allow-downgrades "docker-ce=$PREV" "docker-ce-cli=$PREV"

systemctl is-active docker
docker version --format 'server={{.Server.Version}}'

Three things to know about this command.

--allow-downgrades is required. Modern apt refuses to install an older version of an installed package without it, and the refusal message is not always obvious about the cause.

The daemon restarts again. The downgrade goes through the same postinstall path as the upgrade, so containers take a second interruption. If live restore is on and this is a patch-level downgrade, they survive it; if you are rolling back a line upgrade, they do not.

The old version has to still be published. Docker’s repository keeps a deep history β€” apt-cache madison docker-ce typically lists many prior versions β€” but this is a property of the repository, not a guarantee. Check before you commit to this as your rollback plan, not during the incident.

Configuration changepin
sudo apt-mark hold docker-ce docker-ce-cli containerd.io

# Confirm
apt-mark showhold

# Release the hold when you are ready to try again
# sudo apt-mark unhold docker-ce docker-ce-cli containerd.io

Record the hold somewhere your team reads. A held package is invisible to most patch reporting, so a host pinned during an incident and forgotten becomes a host that quietly stops receiving Docker security updates. Put an expiry date on it in the ticket.

What does not roll back with the packages

Snapshot restore

If you took a filesystem snapshot with the daemon stopped β€” the procedure in the capture lesson β€” this is the most complete rollback available short of rebuilding.

Data-loss risksnapshot restore
# Stop the socket first, or systemd re-activates the daemon
sudo systemctl stop docker.socket docker.service
sudo systemctl stop containerd.service

# Restore the snapshot (LVM example; adapt to your storage)
sudo lvconvert --merge /dev/vg0/docker-preupgrade

# Downgrade the packages to match the restored state
sudo apt-get install -y --allow-downgrades 'docker-ce=5:28.1.3-1~ubuntu.24.04~noble' 'docker-ce-cli=5:28.1.3-1~ubuntu.24.04~noble'

sudo systemctl start containerd.service docker.service
docker ps -a

The order matters: restore the state, then install the binaries that match it. Starting a new daemon against restored old state is the mismatch you are trying to avoid.

The LVM line is illustrative and the mechanics differ by storage stack. lvconvert --merge cannot complete while the origin volume is in use β€” it schedules the merge for the next activation, so the filesystem must be unmounted, or the host rebooted, before the rollback actually takes effect. ZFS (zfs rollback) and hypervisor snapshots have their own constraints. Rehearse whichever one you have on a host that is not production, because β€œrestore the snapshot” is exactly the kind of step that turns out to need a reboot at the worst moment.

Rebuild from capture

The strategy that always works, and the reason the capture lesson comes before this one.

Service impact possiblerebuild
# On the replacement host, install the exact prior version
sudo apt-get install -y 'docker-ce=5:28.1.3-1~ubuntu.24.04~noble' 'docker-ce-cli=5:28.1.3-1~ubuntu.24.04~noble' containerd.io docker-buildx-plugin docker-compose-plugin
sudo apt-mark hold docker-ce docker-ce-cli

# Replay the captured daemon configuration
sudo install -m 0644 ./capture/daemon.json /etc/docker/daemon.json
sudo dockerd --validate
sudo systemctl restart docker

# Re-pull the exact images by digest from the capture
awk '$2 ~ /^sha256:/ {split($1,a,":"); print a[1]"@"$2}' ./capture/images.txt | xargs -r -n1 docker pull

# Bring the workload back from its declarative source
docker compose -f /srv/app/compose.yaml up -d

# Restore volume data from the backup, then verify
docker compose -f /srv/app/compose.yaml ps

This is slower than a downgrade and it is the only strategy that is unambiguously correct. Everything it depends on β€” the version string, the daemon config, the image digests, the Compose file, the volume backup β€” came out of the pre-upgrade capture.

Deciding which one

SymptomStrategy
Daemon will not start; journalctl names a config keyFix the config. Not a rollback.
Daemon starts; a specific feature regressedPackage downgrade of docker-ce only
Containers behave oddly after a containerd movePackage downgrade of both, or rebuild
Image store or storage driver changedRebuild β€” do not fight the state
Host will not bootSnapshot restore or rebuild
Data on the same filesystem as /var/lib/dockerNever snapshot-restore without accepting the data loss

The first row is the most common and it is not a rollback at all. Check journalctl -u docker and dockerd --validate before deciding the upgrade was bad; a large share of β€œfailed upgrades” are a daemon.json key that the new version stopped accepting, which is a two-minute fix.

Knowledge check

Knowledge check Β· 4 questions

  1. Q1. Why does a routine rollback downgrade docker-ce but usually leave containerd.io alone?

  2. Q2. You downgrade Docker successfully during an incident. What must you do immediately afterwards?

  3. Q3. Which of these does a package downgrade NOT restore? Select all that apply.

  4. Q4. Restoring an LVM snapshot of /var/lib/docker also rolls back the contents of every named volume on that filesystem.

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