Docker Engine 29.9.3 went stable on 3 November 2026, and it carries the largest security batch of the 29.9.x line: four Engine and SwarmKit CVEs plus two BuildKit advisories.[1][2] If you have been tracking the 29.x security story you know by now that this line has been shipping steady fixes since 29.9.0 on 8 October - and this release is the one to land on today.[1] This article reworks our earlier 29.9.0 write-up into the full history of the line and gives you a practical upgrade path from 29.9.0 to 29.9.3 on a self-hosted Debian/Ubuntu VPS, with the two spots that deserve special attention: firewalld plus encrypted Swarm overlays, and worker/manager TLS certificate rotation.[2]
If you are still choosing where the host lives, the digitalocean vs linode vs vultr comparison covers the entry-tier cloud choice. This upgrade is version-driven, so the distro matters more than the provider.
The 29.9.x line at a glance
Before the 29.9.3 deep dive, here is what the line shipped, because the correct upgrade path depends on where you started.[1][2][9][11][12]
- 29.9.0 (8 October 2026) - a security release built around the Go toolchain. It bumped Go runtime to 1.26.9 and closed five Go CVEs, four of them remote HTTP/2 denial-of-service bugs (daemon crash via the HPACK encoder, memory exhaustion via
Trailerheaders, a flow-control bypass via stream resets, and CPU exhaustion via window-size churn), plus one Windows-only junction escape. It also added lazy-pull in the containerd image store and a batch of real fixes for encrypted overlay and IPsec networking. - 29.9.1 (14 October 2026) - a containerd-driven release. It shipped containerd v2.4.2, which fixed three image-pull problems including CVE-2026-95839, GHSA-8647-2jxj-p2rc, and GHSA-hc94-p972-ch66.[9]
- 29.9.2 (20 October 2026) - a Go-runtime release. It updated Go runtime to 1.26.10 to fix CVE-2026-111943: rejecting certificate chains through eight revoked eMudhra intermediate TLS certificates. Pulls and pushes to any registry whose chain relies on those intermediates now fail TLS verification.[11]
- 29.9.3 (3 November 2026) - the cumulative security batch: two Engine CVEs, one Swarm overlay CVE, one SwarmKit CVE, and two BuildKit advisories, plus the daemon-hang and CLI YAML bugfixes.[1][2]
If you are still on 29.8.x or on the 28.x line, every one of these applies to you, because the 29.8.2 batch, the 29.8.x fixes, and the whole 29.9.x sequence are all rolled up by the time you reach 29.9.3.
What 29.9.3 fixes
The 29.9.3 security batch splits across the Engine, SwarmKit, and the bundled BuildKit builder.[1][2]
Excessive CPU parsing crafted RSA keys (CVE-2026-116587, GHSA-mgcc-cww5-fp2q). A crafted RSA key could drive excessive CPU use when the engine parsed it.[1][3] For a deploy host the practical shape is a denial-of-service: an input you control parsing, a key you did not author, can pin the daemon. It is fixed in 29.9.3.
Panic when decompressing zstd data (CVE-2026-116591, GHSA-fpmh-frq8-7rj8). A crafted zstd stream can cause a panic while the engine decompresses it.[1][4] A panic in the daemon is an availability hit - the daemon can crash, taking your workloads’ management plane with it (containers keep running, but you lose the ability to manage them until the daemon is back). Fixed in 29.9.3.
Swarm overlay loses encryption and spoofing protection after a firewalld flush (CVE-2026-115538, GHSA-6jhp-7446-hf7f). This is the one to centre if you run firewalld on the host, which is the default on Fedora, RHEL-family, and any VPS image that boots with firewalld active. When firewalld flushes Docker’s iptables rules, the encrypted overlay’s protections drop - the missing encryption and spoofing protections mean overlay traffic is no longer enforced as encrypted/authenticated the way it should be.[1][5] The classic trigger is a firewall-cmd --reload or an unrelated ruleset reload that clears the Docker-managed chains; after that, overlay traffic loses the confidentiality/authenticity guarantees you assumed. On a self-hosted VPS this is precisely the “my encrypted overlay is silently not encrypted” failure mode that is hard to notice until you audit the ruleset. Fixed in 29.9.3.
Worker can mint a manager certificate after a swarm leader change (CVE-2026-115539, GHSA-628g-hv7f-pgjc, SwarmKit). An attacker holding a valid worker-node TLS certificate and private key could obtain a manager-node certificate shortly after a Swarm leader change.[1][6] The escalation matters because a manager certificate lets you issue certificates and change swarm state, not just run tasks. This one argues for treating worker credentials as sensitive and for rotating them on any node you suspect - and for upgrading every node, not only managers, because SwarmKit lives on workers too. Fixed via the SwarmKit update in 29.9.3.
BuildKit v0.34.2 closes two advisories. The bundled builder (the default in buildx) updates to v0.34.2, which fixes:
- GHSA-2fqp-xg6f-v7gj - resolver cache poisoning / daemon crash. A build that resolves metadata for the same Git source reference twice can crash the BuildKit daemon through resolver cache poisoning.[1][7] This is a crash, so the availability angle applies wherever you run untrusted builds.
- GHSA-w26x-xg27-7qjc - xattr escape. A malicious client with access to the BuildKit control APIs could set extended attributes on files outside the BuildKit state directory - potentially in
trusted.orsecurity.scope, which can grant a program on the host more privileges than it has on its own.[1][8] The advisory notes this only matters if you run a build service that accepts builds from untrusted users, but if you run CI or a shared build endpoint on the box, patch it and stop exposing BuildKit control API to the network.[8]
Bugfixes worth knowing. 29.9.3 also fixes the daemon hanging on container, image, and system API requests under load (moby/moby#54041) and literal << keys in YAML files being emitted without quotes (docker/cli#7461).[1][2] The first one is a real self-hosted annoyance: under load the engine could stall on normal API calls; the second prevents a reload-of-config footgun with << anchors.
Why the Go-runtime releases matter
29.9.0 and 29.9.2 are security releases built on the Go toolchain, and it is worth re-stating the mechanism because it explains why a “runtime” bump is a security release at all.[1][11][12]
The engine is written in Go, and Docker bakes a specific Go toolchain into every build. When vulnerabilities land in Go’s standard library, the fix reaches your daemon as a recompile against a patched Go, not as an engine patch. That is 29.9.0’s four HTTP/2 CVEs (CVE-2026-97032 crash, 78659 memory, 78663 flow-control, 78669 CPU) - all reachable from a peer that talks HTTP/2 to your daemon, which the Engine API and BuildKit’s gRPC frontend both are - and 29.9.2’s certificate-chain rejection for the compromised eMudhra intermediates (CVE-2026-111943, via Go 1.26.10).[1][11][12] Because these ride on the toolchain, you cannot see the fix in docker version component output; the Server.Version number is your only reliable signal that you are on the recompiled engine. The Go runtime changes are cumulative, so you get all of them (and 1.26.9’s fixes) simply by reaching 29.9.3.
The 29.9.0 -> 29.9.3 upgrade checklist for a VPS
Here is the concrete path on a self-hosted Debian/Ubuntu host, with the two areas that deserve deliberate attention.
1. Confirm your current version.
docker version --format '{{.Server.Version}}' # the engine's own version, not the CLI's
docker info | grep -i "Server Version"
If it reads 29.9.3, you are done. If you are on 29.9.0, 29.9.1, or 29.9.2, everything below still applies. If you are on 29.8.x or older, you are missing all of the above. However you upgrade from Docker’s apt repository on Debian/Ubuntu:
# maintenance window - this restarts the daemon and your containers
sudo apt-get update
sudo apt-get install --only-upgrade docker-ce docker-ce-cli containerd.io
Then verify:
docker version --format '{{.Server.Version}}' # expect 29.9.3
docker buildx version # expect BuildKit v0.34.2+
docker ps # all workloads back with correct state
Note that --only-upgrade grabs the whole chain: containerd.io (to v2.4.2) and buildx’s bundled BuildKit (to v0.34.2) come with the engine, so one command reaches the cumulative fix set.
2. Firewalld + encrypted Swarm overlay: audit and re-verify encryption.
Because CVE-2026-115538 is triggered by a firewalld flush of Docker’s iptables rules, treat any firewall-cmd --reload, or any ruleset change, as a reason to confirm the overlay is actually encrypted again after this upgrade.[1][5] After moving to 29.9.3, re-init the encrypted networks cleanly. Concretely, on each node in the swarm:
# confirm the docker zone and overlay state look right
sudo firewall-cmd --get-zones | grep docker
sudo iptables -t nat -L -n | grep -i docker # Docker-managed rules present
docker network ls --filter driver=overlay
On a swarm with --opt encrypted, re-create or re-deploy services so the data-plane encryption rules are re-programmed by the fixed engine, then spot-check that overlay traffic between nodes no longer rides unprotected. If your automation reloads firewalld on any event, add this to the post-reload checklist - it is exactly the class of incident this CVE describes.
3. Rotate worker and manager TLS certificates.
CVE-2026-115539 (worker can mint a manager certificate shortly after a leader change) means worker credentials are a trust boundary, not a token to neglect.[6] After upgrading every node, rotate node certificates on any node whose key you do not trust, and regenerate the cluster’s CA material is overkill for most homelabs - a targeted rotate is right:
# on a manager, force node certificate rotation for a node
docker node update --cert-expiry 2160h <node> # existing expiry policy
# on the node itself, renew its own cert pair
sudo systemctl restart docker
docker node ls # confirm all nodes Ready/Reachable
In a single-manager homelab the window is narrow, but paragraph the behaviour: upgrade all nodes in the same maintenance window (managers and workers), because SwarmKit runs on workers too and the fix only takes effect once every node is patched.[1][6]
4. Restart one node at a time and pin your previous version.
Because every node upgrade restarts the daemon and renegotiates overlay state, roll through one node at a time and let services settle before moving on. Keep the previous engine version pinned in your automation so a failed upgrade rolls back fast, and test on a staging host first if you use custom daemon flags, unusual snapshotters, or a heavily customised iptables ruleset - firewalld interactions are exactly where this release bites.
Update now if…
Run down this list and update if:
- you use Swarm encrypted overlay networks and firewalld is (or could be) in the picture - CVE-2026-115538 directly targets the flush-then-loss-of-encryption failure mode;[5]
- a node’s worker TLS key is not something you fully trust - CVE-2026-115539 turns a valid worker key into a manager certificate after a leader change;[6]
- you run any build service that accepts builds from untrusted users - both BuildKit advisories matter, and the xattr escape can reach host privilege;[7][8]
- your Engine API or a service on it is reachable over the network by anything you do not fully trust - the four HTTP/2 and the RSA/zstd DoS CVEs are all available-input classes;[1]
- you pull from or push to a registry whose TLS chain might touch the revoked eMudhra intermediates - that check only lives in 29.9.2+.[11]
Update when convenient rather than urgently if you are a single, non-network-exposed host with no encrypted overlays, no build endpoint, and fully trusted local credentials - the cumulative Go-runtime and pull-path fixes are still worth taking, but your exposure to this particular batch is narrow.
Sources
- Docker - Engine 29 release notes (29.9.3, 2026-11-03: CVE-2026-116587, CVE-2026-116591, CVE-2026-115538, SwarmKit CVE-2026-115539, BuildKit v0.34.2 advisories, moby#54041 daemon hang, cli#7461 YAML
<<keys). https://docs.docker.com/engine/release-notes/29/ - checked 2026-11-05 - GitHub - moby/moby release
docker-v29.9.3(published 2026-11-03). https://github.com/moby/moby/releases/tag/docker-v29.9.3 - checked 2026-11-05 - GitHub - GitHub Advisory GHSA-mgcc-cww5-fp2q (CVE-2026-116587, excessive CPU parsing crafted RSA keys). https://github.com/advisories/GHSA-mgcc-cww5-fp2q - checked 2026-11-05
- GitHub - GitHub Advisory GHSA-fpmh-frq8-7rj8 (CVE-2026-116591, panic decompressing zstd data). https://github.com/advisories/GHSA-fpmh-frq8-7rj8 - checked 2026-11-05
- GitHub - moby/moby release
docker-v29.9.3(CVE-2026-115538, missing encryption/spoofing protections for Swarm overlay after firewalld flushes Docker iptables rules; Swarm overlay advisory GHSA-6jhp-7446-hf7f). https://github.com/moby/moby/releases/tag/docker-v29.9.3 - checked 2026-11-05 - GitHub - moby/moby release
docker-v29.9.3(CVE-2026-115539, worker TLS certificate can obtain a manager certificate shortly after a swarm leader change; SwarmKit advisory GHSA-628g-hv7f-pgjc). https://github.com/moby/moby/releases/tag/docker-v29.9.3 - checked 2026-11-05 - GitHub - moby/buildkit security advisory GHSA-2fqp-xg6f-v7gj (resolver cache poisoning, daemon crash; fixed v0.34.2). https://github.com/moby/buildkit/security/advisories/GHSA-2fqp-xg6f-v7gj - checked 2026-11-05
- GitHub - moby/buildkit security advisory GHSA-w26x-xg27-7qjc (xattr set outside BuildKit state directory, possible host privilege escalation). https://github.com/moby/buildkit/security/advisories/GHSA-w26x-xg27-7qjc - checked 2026-11-05
- GitHub - moby/moby release
docker-v29.9.1(published 2026-10-14; containerd v2.4.2 with CVE-2026-95839, GHSA-8647-2jxj-p2rc, GHSA-hc94-p972-ch66). https://github.com/moby/moby/releases/tag/docker-v29.9.1 - checked 2026-11-05 - GitHub - containerd security advisory GHSA-8647-2jxj-p2rc (daemon crash with crafted Content-Length when parallel layer fetch is enabled via
concurrent_layer_fetch_buffer; fixed 2.4.2). https://github.com/containerd/containerd/security/advisories/GHSA-8647-2jxj-p2rc - checked 2026-11-05 - GitHub - moby/moby release
docker-v29.9.2(published 2026-10-20; Go runtime 1.26.10, CVE-2026-111943 revoked eMudhra TLS intermediates). https://github.com/moby/moby/releases/tag/docker-v29.9.2 - checked 2026-11-05 - GitHub - moby/moby release
docker-v29.9.0(published 2026-10-08; Go runtime 1.26.9, four HTTP/2 CVEs plus Windows junction escape, lazy-pull, networking fixes). https://github.com/moby/moby/releases/tag/docker-v29.9.0 - checked 2026-11-05 - golang/go - issue #82345 for CVE-2026-111943 (denylist of eight compromised eMudhra SPKI in
crypto/x509, fixed Go 1.26.10 / 1.27.3). https://go.dev/issue/82345 - checked 2026-11-05