Docker audit fix

How to Check and Update Docker Engine Versions on Linux

Docker, containerd and runc share the host kernel with every container, so an unpatched runc turns an escape CVE like Leaky Vessels into host root. Apply pending docker, containerd and runc packages, then restart the daemon.

Hiren KalariyaLast reviewed: Oct 19, 2026Check engine-updates
High
severity
No
needs root or sudo
4
results it can return
4 of 5
checks in this audit

Every threshold on this page is transcribed from the docker-security-network-supply-chain audit script that CtrlOps runs, and a build check fails if the two ever disagree. See all 5 Network & Supply Chain checks, or how the audit runs.

High severityNo sudo needed

What this check reads

It asks the first package manager it finds, trying apt-get, then dnf, yum, zypper and apk, which updates it would install, and filters that simulation for docker|containerd|runc. On apt the simulation is apt-get -s -o Debug::NoLocking=true upgrade and the matching ^Inst lines are counted twice: once for the total, and once for the lines that also contain security. No matching line at all is the PASS, one or more security lines is the FAIL, and any other pending line is the WARN. The other package managers return only a total, so on those a pending update can never be worse than a WARN.

When it applies

Runs on any host with a supported package manager (apt-get, dnf, yum, zypper, apk) and the docker binary present; without the binary it SKIPs "Docker not installed", without a package manager it SKIPs too. No root is needed. It filters pending updates for docker|containerd|runc and, as with the database-patch check, reads the cached package lists - a stale apt update gives a stale answer. Only the apt branch distinguishes security updates and can therefore FAIL; on dnf/yum/zypper/apk every pending update is a WARN. Docker installed from a static binary, a Snap, or a convenience script outside the package manager is invisible.

What each result means

Result thresholds this check applies
ResultWhenWhat it means
SKIPDocker not installed / no package managerNo pending-update list was consulted, so the container stack's patch level is unknown. Either no docker binary is present, or none of apt-get, dnf, yum, zypper and apk was found.
PASSNothing pendingThe package lists on this host show no waiting docker, containerd or runc update. The answer is only as fresh as the last apt update or equivalent refresh.
FAILapt: security-pocket update pendingA waiting container-stack update is marked as a security fix, the class that includes the runc escapes that give host root. Patch it now.
WARNOther pending updatesContainer-stack updates are waiting but none is tagged as a security fix. On dnf, yum, zypper and apk every pending update lands here, security or not, so read the release notes.

Why it matters

Containers share the host kernel and depend on runc/containerd for isolation. Escapes are real and recurring: runc CVE-2019-5736 (overwrite host runc), CVE-2024-21626 "Leaky Vessels" (fd leak → host filesystem), and in November 2025 three more runc escapes (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881, fixed in runc 1.2.8 and 1.3.3), with containerd and BuildKit CVEs in the same family. CIS Docker 1.1.1/1.2.1 "Ensure the container host has been hardened / Docker is up to date"; NIST SP 800-190 4.5 (host OS).

Why it fails, and when it is wrong

  • Docker's official apt repository (download.docker.com) does not use a -security pocket, so updates from it always land in WARN, never FAIL, even when they fix a CVE. Read the release notes.
  • Ubuntu's docker.io package comes from universe/-updates, which sometimes carries security fixes without the -security tag.
  • Snap-installed Docker (snap list docker) and static binaries are not seen.
  • Stale apt lists (no recent apt update) produce false PASS.
  • A PASS is about pending packages, not installed versions. A host whose repository simply has nothing newer, for example a distribution docker.io frozen on an old runc, passes while still exposed. Check the installed runc --version against the advisories as well.

How to fix it

Refresh the package lists

The check reads the cached lists, so a stale cache reports nothing pending.

sudo apt-get update
apt-get -s upgrade | grep '^Inst ' | grep -iE 'docker|containerd|runc'

Upgrade the container stack

Upgrade the engine and its runtime together. On Debian and Ubuntu with Docker's repository:

sudo apt-get install --only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

On RHEL and its rebuilds, sudo dnf upgrade docker-ce docker-ce-cli containerd.io. With the distribution's own packages, upgrade docker.io, containerd and runc instead.

Make sure the daemon runs the new version

docker version --format 'client {{.Client.Version}} server {{.Server.Version}}'
sudo systemctl restart docker      # only if the server version is still the old one

A restart stops every running container, and only those with an always or unless-stopped restart policy come back on their own, unless "live-restore": true is set in daemon.json. Live restore only covers patch releases, not a jump to a new major version.

Check runc against the advisories

A PASS means nothing is pending, not that what is installed is safe.

runc --version
# expected: 1.2.8 or later, or 1.3.3 or later on the 1.3 line

Older than that is exposed to the November 2025 escapes (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881), and older than 1.1.12 to CVE-2024-21626 as well.

Verify the fix

sudo apt-get update
apt-get -s -o Debug::NoLocking=true upgrade | grep '^Inst ' | grep -iE 'docker|containerd|runc'
# expected: no output

# RHEL family, including the security-only view the check cannot give you:
dnf -q check-update 'docker*' 'containerd*' 'runc*'; dnf updateinfo list security

# Installed versions matter more than "no pending updates" - check against known escapes:
docker version --format '{{.Server.Version}}'; runc --version; containerd --version
# runc must be 1.2.8+ or 1.3.3+ (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881)

# A package upgrade is not live until the daemon restarts:
systemctl status docker --no-pager | head -3

Debugging

The docker binary is absent, so the check does nothing even if containerd is present and outdated.

Or Docker was installed outside the package manager (static binary, Snap). Track versions manually against Docker's release notes; no package check will see it.

The package lists are probably stale, or the packages are held. sudo apt-get update first, and check apt-mark showhold / dnf versionlock list.

Expected: only the apt branch inspects the archive pocket. Use dnf updateinfo list security to classify it.

The finding clears as soon as the packages are installed, because it reads pending packages, not the running daemon. If it persists, the package lists or a hold are the cause (see above). If it has cleared but docker version still reports the old server version, the daemon was not restarted onto the new binaries: run sudo systemctl restart docker. That stops every running container, and only those with an always or unless-stopped restart policy come back on their own, unless "live-restore": true is set in daemon.json. Live restore only carries containers across patch releases of the daemon, not across a jump to a new major version. Containers already running keep the runtime they started with until they are restarted.

runc and containerd bugs are container escapes: CVE-2019-5736, CVE-2024-21626 and the three November 2025 runc advisories all give host root from inside a container, regardless of how carefully the container itself is configured. Patch these ahead of application updates.

Sources

How the script reads this

Next

Re-run the Network & Supply Chain audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 5 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.

All 5 Network & Supply Chain fixes
Audit your fleet

Find every one of these on every server, in one click

CtrlOps runs this audit over your existing SSH connection - no agents, no scripts to manage. $7/user/month after a 1 month free trial - no credit card required.

Windows

✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals