Docker Engine, containerd and runc: keeping the host patched

Updated November 2026

A Docker host has three pieces of software that need security updates of their own: Docker Engine (dockerd), containerd and runc. Keep all three on a maintained release line, update them with your package manager, and never let the Docker API listen on TCP port 2375.

This page is about the host. Patching what runs inside containers means rebuilding images; that is covered in Patching containers.

What runs on a Docker host

The docker command is only a client. It talks to dockerd, which asks containerd to manage containers, and containerd uses runc to actually create each container with the host kernel. A flaw in any of them can let a container or a remote user reach the host. Flaws in runc and containerd are the classic container escapes.

docker CLIdocker-ce-cli dockerddocker-ce containerdcontainerd.io runccontainerd.io Host kernel: shared by every container, patched by OS updates and a reboot
Package names are those of Docker’s own repository. Distribution packages use other names (see below).

Check installed versions

docker version
containerd --version
runc --version

docker version prints a Client and a Server section; the Server section lists the Engine, containerd and runc versions the daemon is actually using. If it shows an older Engine than your package manager, the daemon was not restarted after the last update. To see which packages are installed and where they came from:

# Debian/Ubuntu
dpkg -l | grep -E 'docker|containerd|runc'
apt-cache policy docker-ce docker.io

# RHEL family, Fedora, SUSE
rpm -qa | grep -E 'docker|containerd|runc'

Supported versions

Docker Engine releases come from the Moby project. Its branch list marks docker-29.x as maintained until after 29’s successor (30.x) ships; 28.x and older are unmaintained and out of scope for security advisories. The latest Engine release is 29.9.2 (October 20, 2026). Moby also lists an older 25.0 branch as maintained until about December 4, 2026; that is a source branch, so only rely on it if your vendor actually ships builds from it.

containerd and runc release lines, as of November 2026
ComponentLineStatusEnd of life
containerd2.4ActiveMay 16, 2027
containerd2.3LTSApril 30, 2028
containerd2.2ActiveNovember 6, 2026
containerd2.0LTS (extended)March 2027
containerd2.1, 1.7, 1.6End of lifealready ended
runc1.5 (latest)bug and security fixesno fixed date
runc1.4 (latest-1)security and significant bug fixesno fixed date
runc1.3 (latest-2)high-severity security fixes onlyno fixed date

containerd 2.0’s extension through March 2027 (LTS means long-term support) is provided by two named maintainers and is aimed at Kubernetes 1.33 on Google Kubernetes Engine, so plan to move off it. containerd 1.7 reached end of life on September 30, 2026. runc ships a new minor version about every six months; each release line moves down one support tier when a new one comes out. runc 1.6.0-rc.1 appeared in October 2026, so when 1.6.0 ships, 1.3 stops getting fixes.

Distribution packages vs Docker’s repository

There are two common ways to get Docker on a server:

Don’t mix the two: Docker’s install guides tell you to remove the distribution packages first, because they conflict.

Update commands

Docker’s repository (Debian and Ubuntu):

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

Distribution packages (Debian and Ubuntu): a normal sudo apt update && sudo apt upgrade includes Docker, containerd and runc. On current Ubuntu releases the containerd daemon is built from the containerd-app source package, so look for that name in Ubuntu notices.

Docker’s repository (RHEL, Fedora and compatible):

sudo dnf upgrade --refresh docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

SUSE (distribution packages):

sudo zypper refresh
sudo zypper update docker containerd runc

Alpine (as root; Docker is in the community repository):

apk update
apk upgrade

FreeBSD: Docker Engine needs a Linux kernel and does not run natively on FreeBSD. If you run it in a Linux virtual machine, patch it there with the commands above.

To roll out a specific version with Docker’s repository, list what is available with apt list --all-versions docker-ce (or dnf list docker-ce --showduplicates) and install that version string, as Docker’s install guides show.

Restarts and live restore

Restarts a service. Check the configuration first with sudo dockerd --validate (it reads /etc/docker/daemon.json; a broken file stops the daemon from starting). A new Engine only takes effect when dockerd restarts, and by default a daemon restart stops the running containers. Containers with a restart policy come back; others don’t. Check after the upgrade and restart it yourself if docker version still shows the old Server version:

sudo systemctl restart docker      # restarts dockerd; containers stop unless live-restore is on
sudo systemctl restart containerd  # after a containerd-only update

On Alpine use rc-service docker restart. To keep containers running during daemon upgrades, enable live restore in /etc/docker/daemon.json:

{
  "live-restore": true
}

Then sudo systemctl reload docker (no container downtime). Docker supports live restore only for patch releases within a line (for example 29.9.1 to 29.9.2), not for upgrades between major versions, and it may fail if you skip releases. It does not cover Swarm services.

runc runs each time a container starts or you docker exec into one, so a fixed runc is used from the next start. When an advisory is about container start-up (most runc escapes are), restart or recreate containers so none were started by the old binary. A kernel update needs a reboot; see kernel updates.

Verify

docker version
docker info | grep -iE 'server version|live restore'
docker ps

Confirm the Server, containerd and runc versions match a fixed release, and that the containers you expect are running.

Never expose port 2375

Anyone who can reach the Docker API can start a privileged container that mounts the host’s disk, which means full root on the server. Port 2375 is the default for the API without TLS: no encryption and no authentication. Docker’s documentation warns that remote access can let non-root users gain root on the host and that remote access without TLS is not recommended. Binding it to localhost doesn’t help much either: every local user and process that can connect gets the same root access.

Check whether the daemon listens on TCP:

sudo ss -ltnp | grep dockerd
grep -s hosts /etc/docker/daemon.json
systemctl cat docker | grep ExecStart

If you see tcp://0.0.0.0:2375 or any other -H tcp:// without TLS, remove it and restart Docker. For remote management, use SSH instead, for example docker -H ssh://admin@server ps, which needs no open Docker port. If you really need TCP, use TLS with client certificates on port 2376 and firewall it to your admin addresses. Docker’s own warning about the client keys: “Guard these keys as you would a root password!”

Was 2375 open to the internet? Treat the host as compromised, not just misconfigured. Look for unknown containers and images (docker ps -a, docker images) and follow cryptominer cleanup and backdoor checks.

Where advisories are published

On GitHub you can “Watch” a repository with custom notifications for security alerts. If you use distribution packages, follow your distribution’s advisories instead (see following security advisories). Current high-profile issues are on our vulnerabilities page.

Hardening tips

Sources