Docker Engine has shipped since version 0.1.0 on 23 March 2013, and the current line is 29.x, with 29.9.2 released on 20 October 2026. Only the docker-29.x and 25.0 branches are maintained upstream, so other versions get no Moby security advisories. Check your version, move to a maintained line, and lock down who can reach the daemon.
That is the short answer. The longer one explains how a single Go binary from a platform company turned into a stack of separately governed projects (Moby, containerd, runc, BuildKit), why the version numbers jumped from 1.13 to 17.03 and then from 20.10 to 23.0, and why the most important security fact about Docker Engine has not changed since 2013: whoever can talk to the daemon controls the host.
This is a release history, not an incident write up, so individual advisories appear only as examples of the process. For the architecture basics, read Docker architecture first; for the desktop product, which updates on a different schedule, see Docker Desktop updates, gradual rollout and the six month window.
Key takeaways
- Docker Engine 0.1.0 shipped on 23 March 2013 and 1.0.0 on 9 June 2014, according to the Docker docs.
- The version scheme changed twice. Docker 17.03.0-ce (1 March 2017) moved to YY.MM calendar versions. Docker 23.0.0 (1 February 2023) moved back to Semantic Versioning, which is why there is no 21 or 22. Since 29.0.0, Engine tags carry a
docker-prefix in the moby/moby repository. - The runtime was split out and handed to neutral foundations. runc went to the Open Container Initiative when it launched on 22 June 2015. containerd was accepted by the CNCF on 29 March 2017 and graduated on 28 February 2019. The open source project behind Docker became Moby on 18 April 2017.
- Security defaults arrived release by release, from the Unix socket (0.5.2) and the default seccomp profile (1.10.0) to stable rootless mode (20.10.0), BuildKit by default (23.0.0), mandatory TLS for remote TCP (27.0) and the containerd image store (29.0.0).
- Support is branch based. The moby/moby BRANCHES-AND-TAGS document lists docker-29.x as Maintained until after docker-30.x and 25.0 as Maintained with an expected end of 4 December 2026. Every other line, including 28.x, gets no upstream advisories.
- The docker group is root. The Docker docs say membership gives root level access.
- Docker Content Trust is being retired. It was removed from the Docker CLI in 29.0.0, and the Notary v1 service at notary.docker.io shuts down on 8 December 2026, according to the Content Trust page.
How did Docker begin, and why did the open source project become Moby?
Docker started inside dotCloud, a platform as a service company. The project was introduced publicly in a lightning talk at PyCon US 2013 titled "The future of Linux Containers", where the speaker described the Linux containers dotCloud used internally (the recording is indexed on PyVideo). The Docker docs' prior releases page records version 0.1.0 on 23 March 2013, followed quickly by 0.1.1 (31 March), 0.1.2 (3 April) and 0.1.3 (4 April 2013).
Those early releases were a single binary that acted as both client and server. Within a few months the project made a choice that still shapes Docker security today. According to the Docker Engine security page, the REST API that the CLI uses to talk to the daemon changed in Docker 0.5.2 to listen on a Unix socket instead of a TCP socket bound to 127.0.0.1. The reason given is that a localhost TCP socket was prone to cross site request forgery if someone ran Docker directly on their workstation. A Unix socket can be protected with ordinary file permissions.
Version 1.0.0 arrived on 9 June 2014. Through the 1.x line, networking, volumes, plugins, Swarm, the builder and logging drivers all lived in one repository, docker/docker.
By 2017 that monolith was a problem for everyone who wanted to build something other than the Docker product from it. On 18 April 2017 Docker announced the Moby Project. The announcement describes Moby as "a library of components", a framework for assembling them into custom container systems, and a place to collaborate, aimed at system builders rather than application developers. It also says that all of Docker's open source collaboration would move to Moby, while Docker itself would remain the product users download. The repository was renamed from docker/docker to moby/moby, and that is where Engine development still happens.
The commercial packaging changed in the same period. The 17.03 release notes in the Docker docs are titled with a -ce suffix (17.03.0-ce, 1 March 2017), the community edition label that ran alongside a commercial edition for several years. In 2026 terms: the code is Moby, and Docker Engine is a build of it that Docker and others ship.
The moby/moby BRANCHES-AND-TAGS document now makes this layering explicit. It lists Moby as the core library (versioned v2.x, still pre-release), Docker Engine as a product built from the repository and versioned independently, and separate Go modules for the API and the client. It also notes that the project provides source code releases, while binaries come from multiple contributing parties.
How did the runtime split apart?
The part of Docker that actually creates containers moved out of the Engine in three steps.
Step 1: libcontainer and runc. Docker originally used LXC as its execution driver and later wrote its own Go library, libcontainer. When the Open Container Initiative launched on 22 June 2015, its overview page says Docker donated its container format and runtime, runC, as "the cornerstone of this new effort". The OCI describes itself as a lightweight open governance structure under the Linux Foundation, formed to create open industry standards around container formats and runtimes. It now maintains three specifications: the Runtime Specification, the Image Specification and the Distribution Specification, the last of which reached v1.0 in May 2020 according to the same page. The deprecated features page in the Docker docs records that the built in LXC exec driver was deprecated in 1.8 and removed in 1.10.
Step 2: Engine 1.11 adopts containerd and runc. The Docker 1.11.0 release notes (13 April 2016) say that "Docker now relies on containerd and runc to spawn containers" and that a Linux installation now consists of four binaries: docker, docker-containerd, docker-containerd-shim and docker-runc. This is the moment the daemon stopped being the process that directly forked containers. dockerd talks to containerd, containerd starts a shim per container, and the shim calls runc, which sets up namespaces and cgroups and then gets out of the way.
Step 3: containerd goes to the CNCF. The CNCF project page states that containerd was accepted on 29 March 2017 at the Incubating level and moved to Graduated on 28 February 2019. The CNCF graduation announcement from that day says containerd was "Born at Docker in 2014" as a lower layer runtime manager for the Engine, that it was the fifth CNCF project to graduate, and that graduation required an independent security audit, a governance structure and a Core Infrastructure Initiative Best Practices badge, which the project completed on 1 September 2018.
Two later Engine releases changed how containerd is managed on the host. The 18.09.0 notes (8 November 2018) explain that before 18.09 containerd was managed by the Engine daemon, while in 18.09 it is managed by systemd. The 23.0.0 notes list "Use a systemd-managed containerd instead of a daemon-managed containerd" in the systemd section. And in 29.0.0 the Engine took the next step: the containerd image store, which keeps images in containerd's content store instead of the Engine's own graph drivers, became the default for fresh installs.
Why does this matter for security? Because each layer now has its own maintainers, release cadence and advisories. A runc fix ships as a runc release first, then lands in Engine packages when the Engine updates its bundled runc. The 29.0.0 notes, for example, list runc v1.3.3, containerd v2.1.5 and BuildKit v0.25.2 as separate updates. For the runtime choices beyond runc, see runc vs crun vs gVisor.
Docker Engine timeline: every era and its security relevant changes
The table below takes every date from the Docker Engine release notes pages on docs.docker.com, the moby/moby GitHub releases list, or the CNCF and OCI pages cited in the sources. "First release" is the first general release of that line, not a release candidate.
| Version or event | Date | What changed that matters for security |
|---|---|---|
| 0.1.0 | 23 Mar 2013 | First public release recorded in the Docker docs |
| 0.5.2 | 2013 | API moved from a localhost TCP socket to a Unix socket, so file permissions control access (Docker Engine security page) |
| 1.0.0 | 9 Jun 2014 | First 1.x release |
| 1.3.2 | 2014 | Images extracted in a chrooted subprocess on Linux (security page) |
| OCI launch | 22 Jun 2015 | Docker donates runc to the Open Container Initiative |
| 1.8.0 | 11 Aug 2015 | Docker Content Trust: trusted pull, push and build, off by default |
| 1.10.0 | 4 Feb 2016 | Default seccomp profile, custom seccomp profiles, --userns-remap daemon flag, --authorization-plugin daemon flag |
| 1.11.0 | 13 Apr 2016 | Containers started through containerd and runc; four binaries instead of one |
| 1.12.0 | 28 Jul 2016 | Swarm mode built in; authorization extended with TLS user information |
| 1.13.0 | 18 Jan 2017 | Managed plugins out of experimental; single --experimental daemon flag |
| 17.03.0-ce | 1 Mar 2017 | YY.MM versioning, monthly and quarterly channels |
| containerd to CNCF | 29 Mar 2017 | Accepted as an incubating project |
| Moby Project | 18 Apr 2017 | Open source development moves to Moby |
| 18.09.0 | 8 Nov 2018 | containerd managed by systemd rather than by dockerd |
| containerd graduates | 28 Feb 2019 | CNCF graduation after an independent security audit |
| 19.03.0 | 22 Jul 2019 | Rootless mode allowed as an experimental feature |
| 20.10.0 | 8 Dec 2020 | Rootless mode graduates from experimental; cgroup v2 support; systemd cgroup driver by default on cgroup v2; seccomp no longer needs libseccomp in dockerd |
| 20.10.24 | 4 Apr 2023 | Last 20.10 patch release listed in the docs |
| 23.0.0 | 1 Feb 2023 | BuildKit and Buildx become the default builder on Linux; SemVer replaces CalVer; alternate OCI runtimes; dockerd --validate |
| 24.0.0 | 16 May 2023 | First 24.0 release |
| 25.0.0 | 19 Jan 2024 | First 25.0 release; the 25.0 branch is still maintained in October 2026 |
| 26.0.0 | 20 Mar 2024 | Remote TCP connections without TLS deprecated, with an explicit warning |
| 26.1.0 | 22 Apr 2024 | Separate 26.1 branch |
| 27.0.1 | 24 Jun 2024 | First 27.0 release (there is no 27.0.0); from 27.0, disabling TLS on a remote TCP listener stops the daemon from starting |
| 28.0.0 | 19 Feb 2025 | Rewritten iptables rules; direct routed access to unpublished container ports blocked; rootlesskit-docker-proxy removed |
| 28.5.2 | 5 Nov 2025 | Last 28.x release listed in the docs |
| 29.0.0 | 10 Nov 2025 | containerd image store default for fresh installs; cgroup v1 deprecated; Content Trust removed from the CLI; experimental nftables backend; docker-v tag prefix; daemon requires API v1.44 or later |
| 29.9.2 | 20 Oct 2026 | Latest release at the time of writing; Go runtime update with a Security section in the notes |
The security page names 0.5.2 and 1.3.2 without dates, so the table gives only the year. The 27.0 line has an odd start: the 27.0 notes explain that "There's no 27.0.0 release" because a tag was created by mistake during the 27.0.0-rc.1 pre-release and had already been picked up by the Go module mirror, so 27.0.1 became the first 27.0 release.
The 1.x era (2013 to January 2017): features land in one repository
The 1.x releases are where most of the Engine's security controls first appeared, usually behind a flag and switched off.
Content Trust in 1.8.0. The 1.8.0 notes (11 August 2015) introduce "Trusted pull, push and build, disabled by default". Content Trust was built on Notary and turned on with the DOCKER_CONTENT_TRUST environment variable. It never became the default, a reminder that a signing feature nobody enables changes little. The 29.0.0 notes say Content Trust was removed from the Docker CLI and can be built as a separate plugin. The Content Trust page now opens with a notice that DCT "is being retired" and that the Notary v1 service at notary.docker.io will shut down on 8 December 2026. If any pipeline still sets DOCKER_CONTENT_TRUST=1, plan its replacement now.
seccomp, user namespaces and authorization plugins in 1.10.0. The 1.10.0 notes (4 February 2016) are one of the densest security releases in Docker's history. They add a default seccomp profile, support for custom seccomp profiles through --security-opt, the --userns-remap daemon flag (previously experimental), and the --authorization-plugin daemon flag for custom access control. The seccomp docs describe the default profile as one that "disables around 44 system calls out of 300+". It uses a default action of SCMP_ACT_ERRNO and allows specific system calls, which means anything not listed fails with a permission error.
Swarm mode in 1.12.0. The 1.12.0 notes (28 July 2016) add Swarm mode, a built in orchestrator. The same release extended authorization plugins with TLS user information and made a denied request return HTTP 403.
The YY.MM era (March 2017 to 2022): calendar versions and channels
The 17.03.0-ce notes say Docker moved to a monthly release cycle with a new YY.MM versioning scheme. Two channels existed. A monthly release received security and bug fixes only until the next monthly release, and a quarterly release received them for four months after its initial release. 17.03.0 itself contained bug fixes for 1.13.1 and kept the same API version, so the jump from 1.13 to 17.03 was a renaming, not a rewrite.
The monthly cadence did not last. The YY.MM numbers of the later lines show long gaps: 18.09.0 in November 2018, 19.03.0 in July 2019 and 20.10.0 in December 2020. 20.10 in particular had a long tail of patch releases, ending with 20.10.24 on 4 April 2023.
The security story of this era is mostly about running the daemon with less privilege.
- 19.03.0 (22 July 2019) lists, under its Experimental heading, that running dockerd as a non root user (Rootless mode) is now allowed.
- 20.10.0 (8 December 2020) lists rootless mode graduating from experimental and adds
dockerd-rootless-setuptool.sh. The same release adds cgroup v2 support and switches to the systemd cgroup driver by default on cgroup v2 when it is available. The 20.10.6 patch release then moved cgroup v2 out of experimental, madedockerd-rootless.shrefuse to run as root, and had the setup tool create a CLI context named rootless. - 20.10.0 also removed dockerd's dependency on libseccomp, added newer system calls such as openat2 and faccessat2 to the default seccomp profile, and changed BuildKit to use Docker's default seccomp profile.
The SemVer era (2023 onward): BuildKit by default and stricter defaults
The 23.0 notes explain the jump directly: from 23.0.0, Docker Engine moves from CalVer to SemVer. The stated reason is Go module compatibility. There is no 21 or 22.
23.0.0 (1 February 2023) made Buildx and BuildKit the default builder on Linux and aliased docker build to docker buildx build. The legacy builder stays available with DOCKER_BUILDKIT=0. The BuildKit page in the Docker docs now says BuildKit "is the default builder for Docker Desktop and Docker Engine users", with the legacy builder still used for Windows containers. The same release added support for alternate OCI runtimes compatible with the containerd runtime v2 API and a dockerd --validate flag that checks daemon.json and exits. For build hardening, see BuildKit security best practices.
24.0.0 (16 May 2023) and 25.0.0 (19 January 2024) continued the containerd image store work, and 25.0 matters in 2026 because it is the one older line still maintained upstream.
26.0.0 (20 March 2024) deprecated "the ability to accept remote TCP connections without TLS" and added an explicit deprecation message when it happens. The deprecated features page records the follow through: in 27.0 and later, setting --tls=false or --tlsverify=false makes the daemon fail to start if it also accepts remote TCP connections. The requirement applies to every TCP address except tcp://localhost.
28.0.0 (19 February 2025) rewrote the iptables rules used for port publishing and network isolation, described in the notes as "a first step in refactoring to enable native nftables support". It also fixed two exposures in how published ports behaved: one let remote hosts connect directly to a container on its published ports, and one let neighbor hosts to reach ports mapped on a loopback address. Direct routed access to unpublished container ports is now blocked in the DOCKER chain. The notes warn that downgrading requires manual cleanup of the new rules, and they removed the rootlesskit-docker-proxy executable.
29.0.0 (10 November 2025) opens with a caution about breaking changes and deprecations. The ones that matter for security and operations:
- The containerd image store is the default for fresh installs. It does not apply to daemons configured with userns-remap, and the notes say the containerd image store is temporarily unavailable when user namespace remapping is enabled.
- cgroup v1 is deprecated; support continues until at least May 2029, and the notes urge migration to cgroup v2.
- Docker Content Trust was removed from the Docker CLI.
- Experimental nftables support can be turned on with the daemon's firewall-backend option.
- Releases are tagged with a
docker-prefix, for example docker-v29.0.0, and the Go module github.com/docker/docker is deprecated in favor of github.com/moby/moby/client and github.com/moby/moby/api. - SwarmKit's internal TLS configuration excludes known insecure cipher suites, and the client config drops legacy CBC cipher suites.
- In rootless mode,
dockerd-rootless.shfalls back to pasta when slirp4netns is not installed.
Is 29.x released? Yes. The Docker Engine version 29 release notes page lists 29.0.0 on 10 November 2025 and minor releases roughly monthly since: 29.1.0 (27 November 2025), 29.2.0 (26 January 2026), 29.3.0 (5 March 2026), 29.4.0 (7 April 2026), 29.5.0 (14 May 2026), 29.6.0 (18 June 2026), 29.7.0 (30 July 2026), 29.8.0 (3 September 2026) and 29.9.0 (8 October 2026). The latest patch at the time of writing is 29.9.2 on 20 October 2026, and the moby/moby GitHub releases list shows the matching docker-v29.9.2 tag on the same day.
How do Engine releases and patch support work today?
Three documents define the current model: the release notes on docs.docker.com, the BRANCHES-AND-TAGS document in moby/moby, and the repository's SECURITY.md.
Versions. Engine versions follow SemVer: a major (29), a minor that adds features (29.9), and patches that fix bugs and security issues (29.9.2). Since 29.0.0 the Git tags are docker-vX.Y.Z. Release candidates are tagged with an -rc.N suffix; the GitHub releases list shows, for example, docker-v29.9.0-rc.1 on 1 October 2026 and rc.2 on 7 October, followed by docker-v29.9.0 on 8 October.
Branches. BRANCHES-AND-TAGS says releases are cut from master when possible, and release branches are created when work on master would conflict with patch releases. A single branch such as docker-29.x can cover a whole major. Historically, 26.0 and 26.1 had separate branches.
Support status. Each branch has one of three contribution statuses:
| Status | What the document says it means |
|---|---|
| Maintained | Actively developed; accepting contributions and backports; in scope for security advisories |
| Maintained (security) | No longer actively developed; may accept backports for critical security issues; in scope for security advisories |
| Unmaintained | No longer actively developed; not accepting contributions; out of scope for security advisories |
The table of branches as fetched on 25 October 2026:
| Branch | Status | Expected end of maintenance |
|---|---|---|
| docker-29.x | Maintained | After docker-30.x |
| docker-28.x | Unmaintained | |
| 27.x | Unmaintained | |
| 26.1 | Unmaintained | |
| 26.0 | Unmaintained | |
| 25.0 | Maintained (by a named sponsoring maintainer) | 4 December 2026 |
| 24.0 | Unmaintained | |
| 23.0 | Unmaintained | 19 May 2025 |
| Older than 23.0 | Unmaintained |
So the practical rule is: the current major is supported until the next major replaces it, and older lines are supported only if a maintainer sponsors them, usually because a downstream distributor ships that line. The GitHub releases list confirms 25.0 is still moving: v25.0.18 was published on 30 September 2026. The document notes that the contribution status "is informational and not binding", so do not treat the 25.0 date as a contract.
Two consequences follow. 28.x stopped getting upstream fixes once 29 took over, even though 28.5.2 shipped only five days before 29.0.0. And if you run 25.0, about six weeks of upstream maintenance remain from the date of this post, so plan the jump to 29.x and read the 29.0.0 breaking changes first.
Packaging versus source. The Moby project releases source. Docker builds and ships the docker-ce packages from download.docker.com, and Linux distributions build their own packages (on Debian and Ubuntu, usually named docker.io). The Docker install guide for Ubuntu calls the distribution packages "unofficial Docker packages" that may conflict with Docker's own. A distribution package can carry backports under an older version number, or simply be behind, so check its changelog rather than docker version alone.
How does the Docker daemon security model work?
The Docker Engine security page breaks container security into four areas: kernel namespaces and cgroups, the attack surface of the daemon, the container configuration profile, and kernel hardening features. This section focuses on the daemon, because that is where the release history above has had the most effect.
The root daemon
The security page states that the daemon "requires root privileges unless you opt-in to Rootless mode". It then explains why only trusted users should control it: Docker lets you share a host directory into a container without limiting the container's access rights, so a container can mount the host's root filesystem and change it. Anyone who can call the API can therefore start a container that reads or writes anything on the host.
This is a design property, not a bug.
The docker group equals root
The post installation guide explains that dockerd creates a Unix socket that members of the docker group can use, so they do not need sudo. Its warning is direct: the docker group "grants root-level privileges to the user". On a shared build host or a jump box, adding developers to the docker group is the same as giving them root. If that is not acceptable, use rootless mode, or give people sudo for specific commands with logging, or move builds to dedicated runners.
The Unix socket, TCP with TLS, and SSH
By default dockerd listens on /var/run/docker.sock. You can add TCP listeners with -H tcp://... or the hosts key in daemon.json. The "Protect the Docker daemon socket" page and the deprecation notes set the rules:
- TCP without TLS is no longer allowed for remote addresses. From 27.0, the daemon fails to start if TLS is disabled on a non-localhost TCP listener.
- With TLS, use
--tlsverifywith a CA, a server certificate and client certificates, so the daemon authenticates clients, not just encrypts traffic. - SSH is a supported alternative. The security page notes you can use
DOCKER_HOST=ssh://USER@HOST, and the protect access page shows how to set this up with a Docker context. - The security page also warns that a TCP endpoint can still be reachable from containers, which can lead to privilege escalation. Bind only where you need it.
Authorization plugins
The authorization plugin page describes Docker's default model as "all or nothing": any user with permission to reach the daemon can run any client command. Authorization plugins, added with the --authorization-plugin flag in 1.10.0, let the daemon ask an external plugin to approve or deny each API request, based on the user, the request and the response. With TLS client certificates, the plugin can see who the user is (the 1.12.0 notes added TLS user information for this purpose). Plugins are a control point, not a sandbox.
Rootless mode
Rootless mode runs both the daemon and the containers as an unprivileged user inside a user namespace. The rootless page lists the prerequisites: newuidmap and newgidmap on the host (usually from the uidmap package), and at least 65,536 subordinate UIDs and GIDs for the user in /etc/subuid and /etc/subgid. Setup uses dockerd-rootless-setuptool.sh install, which since 20.10.6 creates a CLI context named "rootless".
What you gain: a container breakout or daemon compromise lands as an ordinary user, not root. What you give up is documented on the rootless tips page: exposing privileged ports below 1024 needs CAP_NET_BIND_SERVICE set on the rootlesskit binary, and cgroup based limits such as --cpus, --memory and --pids-limit work "only when running with cgroup v2 and systemd". Networking also goes through a user mode network stack, which behaves differently from the rootful bridge. The rootless page also notes that s390x is not supported because static packages are not available for it. Rootless networking keeps changing: 29.0.0 falls back to pasta when slirp4netns is missing. For a deep configuration guide, see rootless mode security.
User namespace remapping (userns-remap)
userns-remap keeps the daemon as root but maps root inside containers to an unprivileged range on the host. Setting "userns-remap": "default" creates a dockremap user and uses its ranges from /etc/subuid and /etc/subgid. The docs give the example of a user with the range starting at 231072 and length 65536, so UID 0 in the container is UID 231072 on the host.
The documented limitations are real. These features are incompatible with userns-remap: sharing PID or network namespaces with the host (--pid=host, --network=host), volume or storage drivers that do not understand the mapping, and --privileged unless you also pass --userns=host. Host volume ownership must be arranged in advance, and root in the container cannot use mknod. In 29.x there is one more: the containerd image store, now the default for fresh installs, is not used when userns-remap is on.
Comparing the options
| Control | Daemon runs as | Container root maps to | Main cost | Introduced (stable) |
|---|---|---|---|---|
| Default (rootful) | root | root (with seccomp, capabilities, AppArmor or SELinux limits) | docker group equals root | 0.x |
| Authorization plugin | root | root | Plugin design and availability | 1.10.0 |
| userns-remap | root | unprivileged subordinate range | Host networking, privileged mode and some drivers unavailable | 1.10.0 |
| Rootless mode | unprivileged user | unprivileged subordinate range | Networking and resource limit differences; per user daemons | 20.10.0 (experimental in 19.03.0) |
| Remote API over TCP with TLS | root | as above | Certificate management | TLS mandatory for remote TCP from 27.0 |
| Remote API over SSH | root | as above | SSH key management | Docker context support |
How does the project publish Engine security fixes?
At the concept level the process has four parts.
Private reporting. SECURITY.md in moby/moby asks reporters not to file public issues and to email security@docker.com instead. It says reporters can expect an acknowledgement within 72 hours. The project does not run a paid bug bounty.
Draft advisory and embargo. After triage, "a draft security advisory will be created on GitHub", used to discuss the issue with maintainers, the reporter and other affected parties under embargo. If the report is accepted, the maintainers set a timeline for the patch, the public disclosure and the patch release.
Patch release and advisory. Fixes ship as patch releases on the maintained branches. The release notes on docs.docker.com now carry a Security heading for releases that fix security issues. The 29.9.2 notes (20 October 2026), for example, have a Security section that explains a Go runtime update and what operators will see as a result, and a Packaging updates section naming the Go version. The matching GitHub Security Advisory is published on the moby/moby advisories page with affected and patched versions. The GitHub API listed 26 published advisories for the repository when we fetched it on 25 October 2026, with severities from low to critical.
Scope. Only Maintained and Maintained (security) branches are in scope for security advisories. An advisory will tell you which maintained versions contain the fix. It will not tell you whether 27.x or 28.x is affected, because those lines are out of scope.
So watch the moby/moby releases and advisories, and read the Packaging updates section of every patch release, because a Go, runc, containerd or BuildKit bump is often the actual fix. For a container image view of the same problem, our post on container image supply chain incidents in 2026 covers the image side.
Step by step: audit a Docker Engine host
Run these on each Linux host that runs Docker Engine. They read state and change nothing until step 7. Where a command needs root, it uses sudo.
1. Read the client and server versions
docker version --format 'client={{.Client.Version}} server={{.Server.Version}} api={{.Server.APIVersion}}'
docker version --format '{{range .Server.Components}}{{.Name}} {{.Version}}{{"\n"}}{{end}}'
The second command lists the Engine, containerd, runc and docker-init versions the daemon reports. Compare the Engine major against the BRANCHES-AND-TAGS table: 29.x and 25.0 are maintained; anything else is not. Compare the patch number against the latest entry on the Docker Engine release notes page.
2. Read the security options, cgroup setup and rootless status
docker info --format 'security={{json .SecurityOptions}}'
docker info --format 'cgroup_driver={{.CgroupDriver}} cgroup_version={{.CgroupVersion}}'
docker info --format 'driver={{.Driver}} root_dir={{.DockerRootDir}}'
docker context ls
SecurityOptions lists the active controls as name entries, for example seccomp with its profile, apparmor or selinux, userns when remapping is on, rootless in rootless mode, and cgroupns. If seccomp is missing, or a profile shows unconfined, find out why. If CgroupVersion is 1, plan the move to cgroup v2 before cgroup v1 support ends (the 29.0.0 notes say not before May 2029). The context list shows whether your CLI is talking to a rootless daemon, a remote host over SSH, or the default socket.
3. Find exposed daemon sockets
# TCP listeners owned by dockerd (2375 is the conventional plain port, 2376 the TLS port)
sudo ss -ltnp | grep -E 'dockerd|:2375|:2376'
# Where the daemon was told to listen
sudo systemctl cat docker.service | grep -E 'ExecStart|-H '
sudo cat /etc/docker/daemon.json 2>/dev/null
# Unix socket permissions and group membership
ls -l /var/run/docker.sock
getent group docker
Any TCP listener on an address other than localhost must use --tlsverify with client certificates, or be replaced by SSH. On 27.0 and later the daemon refuses to start with TLS turned off on a remote TCP address, but older hosts will still run that way. Review every name in the docker group, since each is effectively root.
4. Find containers that mount the daemon socket
docker ps -q | xargs -r docker inspect \
--format '{{.Name}} {{range .Mounts}}{{.Source}} {{end}}' \
| grep -E 'docker\.sock|containerd\.sock'
A container with the socket mounted can start other containers with any settings, including a host root mount, so it is as powerful as root on the host. Some tools need it (CI agents, monitoring), but each one should be a documented, reviewed exception.
5. Check where the packages came from
# Debian and Ubuntu
apt-cache policy docker-ce docker.io containerd.io containerd runc
dpkg -l | grep -E 'docker|containerd|runc'
# RHEL, Fedora and derivatives
rpm -qa | grep -E 'docker|containerd|runc'
dnf repolist | grep -i docker
If you see docker.io from the distribution archive, find the package changelog to learn which upstream fixes it carries. If you see both docker.io and docker-ce, or distribution containerd alongside containerd.io, clean that up; the Ubuntu install guide lists docker.io, docker-doc, docker-compose, docker-compose-v2, podman-docker, containerd and runc as conflicting packages.
6. Verify the repository signing key
Docker's Ubuntu instructions store the repository key at /etc/apt/keyrings/docker.asc and reference it with Signed-By in a deb822 source file:
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Show the key and its fingerprint, then compare with the install guide
gpg --show-keys /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
Compare the fingerprint gpg prints with the one published in the install guide for your distribution before you trust the key. Using Signed-By scopes the key to Docker's repository only, so it cannot sign packages from other sources. On RPM systems, rpm -K on a downloaded package checks its signature against imported keys.
7. Upgrade to a maintained line
Remove conflicting distribution packages first (the guide gives a one line apt remove command that selects only installed ones), then install from the official repository:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl status docker --no-pager
docker version
Before a major upgrade, run dockerd --validate --config-file /etc/docker/daemon.json (since 23.0.0) to catch configuration errors, read the breaking changes for every major you skip, and test on one host first. Moving from 25.0 to 29.x crosses four majors, including the 28.0 firewall rewrite and the 29.0 storage default change.
8. Decide on rootless or userns-remap
For shared build hosts and developer workstations, rootless mode is usually the better fit because it removes root from the daemon. For hosts that run trusted workloads and need host networking or privileged containers, keep the rootful daemon, drop the docker group, and use TLS or SSH contexts and an authorization plugin.
Common mistakes
Running the distribution's docker.io package and assuming it tracks upstream. It might carry backports, or it might be several majors behind. Check the package changelog, not only docker version. If you want upstream patch timing, install from download.docker.com.
Staying on 28.x because it "still works". 28.x became Unmaintained when 29.x took over. No upstream security advisories will cover it.
Putting developers in the docker group on shared hosts. The docs call this root level access. Use rootless mode or dedicated runners instead.
Exposing the API over TCP without TLS. Since 27.0 the daemon refuses to start that way on a remote address, but hosts on older versions or with tcp://0.0.0.0 left over from a tutorial still exist. Use TLS with client certificate verification, or SSH.
Mounting /var/run/docker.sock into containers casually. A socket mount is root on the host. Treat each one as a privileged component with its own review.
Assuming Docker Desktop and Docker Engine update the same way. Desktop bundles its own Engine inside a VM and has its own release notes and update rules. Patching Desktop on laptops does nothing for Engine on servers, and the reverse. See our Docker Desktop update window post.
Relying on Docker Content Trust. It was removed from the CLI in 29.0.0 and the Notary v1 service shuts down on 8 December 2026. Pipelines that set DOCKER_CONTENT_TRUST=1 need a new signing and verification approach.
Skipping majors without reading every breaking change list. The 28.0 iptables rewrite needs manual cleanup to downgrade, and 29.0 changed the default image store, cgroup v1 status and tag format.
FAQ
What is the latest version of Docker Engine?
At the time of writing it is 29.9.2, released on 20 October 2026 according to the Docker Engine version 29 release notes, with the matching docker-v29.9.2 tag on GitHub the same day. Check the release notes page for anything newer before you plan an upgrade.
What is the difference between Docker Engine and Moby?
Moby is the open source project and repository (moby/moby) where the Engine is developed. Docker Engine is the product built from that repository and distributed by Docker and other parties. Since 29.0.0 the repository tags Engine releases as docker-vX.Y.Z to keep them separate from Moby's own library versions.
Why did Docker jump from 1.13 to 17.03, and from 20.10 to 23.0?
17.03 (1 March 2017) introduced YY.MM calendar versioning with monthly and quarterly channels. 23.0 (1 February 2023) returned to Semantic Versioning to move toward Go module compatibility, so 21 and 22 were never used.
Which Docker Engine versions still get security fixes?
According to the moby/moby BRANCHES-AND-TAGS document fetched on 25 October 2026, docker-29.x is Maintained until after docker-30.x, and 25.0 is Maintained with an expected end of 4 December 2026. Everything else, including 28.x, is Unmaintained and out of scope for security advisories.
Is it safe to add my user to the docker group?
Only on a machine where that user could have root anyway. The Docker post installation guide says the docker group grants root level privileges. On shared hosts, use rootless mode instead.
What is the difference between rootless mode and userns-remap?
Rootless mode runs the daemon and containers as an unprivileged user. userns-remap keeps the daemon as root but maps container root to an unprivileged UID range. Rootless removes root from the daemon itself; userns-remap protects the host from container root while keeping the daemon privileged. Both need subordinate ID ranges in /etc/subuid and /etc/subgid.
Can I expose the Docker API over TCP without TLS?
Not on a remote address with current versions. It was deprecated in 26.0, and from 27.0 the daemon fails to start if TLS is disabled on a TCP listener other than tcp://localhost. Use TLS with client certificates, or SSH.
When did BuildKit become the default builder?
In Docker Engine 23.0.0, released on 1 February 2023, for Linux. The legacy builder is still available with DOCKER_BUILDKIT=0 and is still used for Windows containers.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
An honest boundary first. Safeguard does not patch dockerd on your hosts, audit daemon.json, or manage who sits in the docker group; those are host configuration jobs covered by the audit steps above. Where it does help is the software that Docker Engine runs: Safeguard scans container images, repositories and SBOM files for vulnerable components (for example safeguard scan --image your-image:tag, with JSON results under ./output), shows which projects carry a given component and version, evaluates policies and gates in the platform, and can open fix pull requests through autonomous remediation, which ships switched off.
The first demo is a real Safeguard CLI session. You ask it to fix a vulnerable dependency, it reads the finding and the advisory, asks before any command that changes something, edits the manifest, runs the install and tests, and pushes nothing. The same flow applies when an image you build on Docker Engine carries an outdated library: scan the image, ask for the fix, and rebuild.
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with a device code, and then scans from the terminal. On a build host that runs Docker Engine, this is how you scan the images you just built without sending the build context anywhere else.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. When you are planning a move from 25.0 or 28.x to 29.x, you can ask about the findings in the images that run on those hosts in plain language before you start.
Click through it at your own pace, or open it full screen.
To try it on your own images, download Safeguard and follow the CLI docs.
Quick check
Which Docker Engine release lines are in scope for upstream security advisories in late October 2026?
docker-29.x, which is maintained until after docker-30.x, and 25.0, which has an expected end of maintenance on 4 December 2026. Every other line, including 28.x, is Unmaintained and out of scope.
What happens if you start Docker Engine 27.0 or later with a remote TCP listener and TLS disabled?
The daemon fails to start. Unauthenticated remote TCP was deprecated in 26.0 and enforced from 27.0; only tcp://localhost is exempt. Use TLS with client certificates or SSH instead.
Sources
- Docker Engine version 29 release notes
- Docker Engine 28 release notes
- Docker Engine 27 release notes
- Docker Engine 26.1 release notes
- Docker Engine 26.0 release notes
- Docker Engine 25.0 release notes
- Docker Engine 24.0 release notes
- Docker Engine 23.0 release notes
- Docker Engine 20.10 release notes
- Docker Engine 19.03 release notes
- Docker Engine 18.09 release notes
- Docker Engine 17.03 release notes
- Docker Engine prior releases (0.1.0 to 1.13)
- Deprecated Docker Engine features
- Docker Engine security
- Protect the Docker daemon socket
- Rootless mode
- Rootless mode tips
- Isolate containers with a user namespace
- Seccomp security profiles for Docker
- Content trust in Docker
- Access authorization plugin
- BuildKit
- Install Docker Engine on Ubuntu
- Linux post-installation steps for Docker Engine
- moby/moby GitHub releases
- moby/moby BRANCHES-AND-TAGS.md
- moby/moby SECURITY.md
- moby/moby security advisories
- Introducing the Moby Project (Docker blog, 18 April 2017)
- Open Container Initiative overview
- containerd project page (CNCF)
- CNCF announces containerd graduation (28 February 2019)
- Lightning Talk: The future of Linux Containers, PyCon US 2013 (PyVideo)
Last reviewed: 25 October 2026. Docker Engine ships patch releases often and branch status changes when a new major arrives; check the release notes and BRANCHES-AND-TAGS.md before planning an upgrade.