Skip to main content
RunBook Academy

Docker & ContainersXXIX Β· Docker UpgradesUpgrade anatomy

What actually moves in a Docker upgrade

Intermediate⏱ ~22 mindocker

What you'll learn

  • Name the five packages a Docker installation is made of and what each contains
  • Read `docker version` and identify which component owns a given behaviour
  • Explain client/server API negotiation and when version skew matters
  • Predict which containers are disturbed by upgrading which component

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.

β€œUpgrading Docker” sounds like one operation on one thing. It is up to five packages, holding four independently versioned binaries, and the operational consequences differ sharply depending on which of them actually changed.

This lesson is the map. The next five lessons are the procedure.

The five packages

On a Debian or Ubuntu host installed from Docker’s repository:

PackageContainsRestarting it affects
docker-cedockerd, the daemonThe control plane; containers only if live restore is off
docker-ce-clithe docker commandNothing running β€” it is a client binary
containerd.iocontainerd and runcThe supervisor; running shims survive
docker-buildx-plugindocker buildxBuilds only
docker-compose-plugindocker composeNothing running β€” it is a client

Two of those five β€” the CLI and the Compose plugin β€” cannot disturb a running container at all. They are client-side binaries that talk to the daemon over a socket. Upgrading them during business hours is a non-event.

The containerd.io package is the one people misread. It carries both containerd and runc, which is why a runc CVE fix arrives as a containerd.io version bump with no change to the Docker version at all.

Reading the versions

Read-only / Safedocker version
$ docker version
Client: Docker Engine - Community
Version:           29.7.1
API version:       1.55
Go version:        go1.26.5
Git commit:        e9452d6
OS/Arch:           linux/amd64
Context:           default

Server: Docker Engine - Community
Engine:
Version:          29.7.1
API version:      1.55 (minimum version 1.40)
Go version:       go1.26.5
OS/Arch:          linux/amd64
Experimental:     false
containerd:
Version:          v2.2.6
GitCommit:        11ce9d5f3c68c941867e82890e93e815c1304f1b
runc:
Version:          1.3.6
GitCommit:        v1.3.6-0-g491b69ba
docker-init:
Version:          0.19.0
GitCommit:        de40ad0

Illustrative output

Four version numbers under Server, and they move on independent schedules:

  • Engine β€” Docker’s own version, calendar-based (YY.MM.patch).
  • containerd β€” upstream containerd’s semantic version.
  • runc β€” upstream runc’s semantic version. This is the one that matters for container-escape CVEs.
  • docker-init β€” the tini-derived init used by --init. Rarely changes.

docker info gives you the same versions plus the runtime configuration; use whichever you prefer, but get into the habit of recording all four before an upgrade rather than just the Engine version.

The version scheme

Docker Engine uses calendar versioning: 28.3.2 is the third feature release of 2028’s cycle line, patch 2. The important consequence for operations is that the middle number is a feature release, not a minor version in the semver sense. A move from 28.2.x to 28.3.x can add flags, change defaults, and deprecate things.

Read-only / Safeavailable versions
apt-cache madison docker-ce
apt-cache madison containerd.io
apt-cache policy docker-ce

madison lists every version in the configured repositories, newest first. policy shows what is installed against what is the candidate. Between them you know exactly what an unpinned apt-get upgrade would do.

Client and server skew

The CLI and the daemon negotiate an API version on every connection. The client offers its version; the daemon replies with the highest it supports; the client uses the lower of the two.

This is why upgrading docker-ce-cli alone is safe, and also why a host that has been upgraded piecemeal for years can end up with a CLI several feature releases ahead of its daemon without anyone noticing. docker version with both blocks visible is the check.

Which component disturbs what

The operational core of the lesson:

Component upgradedRunning containersNetworkingBuildsIn-flight docker exec
docker-ce-cliUntouchedUntouchedUntouchedUntouched
docker-compose-pluginUntouchedUntouchedUntouchedUntouched
docker-buildx-pluginUntouchedUntouchedInterruptedUntouched
docker-ce (daemon)Survive only with live restoreRules re-appliedInterruptedDropped
containerd.ioSurvive β€” shims are separate processesUntouchedInterruptedDropped

The two rows worth memorising are the last two, and the asymmetry between them surprises people: upgrading containerd is less disruptive to running containers than upgrading the daemon, because the shim that supervises each container is a separate process that outlives a containerd restart, whereas containers die with the daemon unless live restore is enabled.

Knowledge check

Knowledge check Β· 4 questions

  1. Q1. A runc CVE is announced. Which package version bump delivers the fix on a Debian or Ubuntu host?

  2. Q2. What happens to running containers when only `containerd.io` is upgraded and containerd restarts?

  3. Q3. Which packages can be upgraded without any risk to a running container? Select all that apply.

  4. Q4. Upgrading containerd.io means every running container is immediately using the new shim and runc binaries.

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