Why Docker v29 breaks existing stacks — and why now
An agency managing a fleet of client VPS is caught in a particular bind: the upstream update (Docker, Ubuntu, Debian) arrives from outside, regardless of who administers the server. When a client VPS receives apt upgrade without oversight, three breaking changes arrive simultaneously.
First break — minimum API version. Docker Engine v29 raises the minimum API version to 1.44. Clients still running the old docker-compose v1 standalone binary (/usr/local/bin/docker-compose) fail immediately: their client code is compiled for earlier API versions, and the daemon refuses the connection with an incompatibility message.
Second break — nftables network backend. Docker v29 enables nftables by default instead of iptables to manage Docker network firewall rules. Third-party scripts that inspect iptables chains directly (monitoring scripts, custom firewalls, certain UFW rules) no longer see Docker rules — not because they disappeared, but because they now live in nftables.
Third break — containerd image store. The image storage backend switches to the containerd store. Opt-in since v29, it will become the default in v30. On a migrated server, existing images remain accessible but the cache path changes, which can surprise scripts that inspect /var/lib/docker/image directly.
The classic objection — "our clients manage their own VPS" — provides no protection. The break comes from outside, and the agency is who the client calls when their site stops responding.
What this guide enables you to do
- Detect the API version tension between your
dockerclient and the VPS daemon, before the next command produces a cryptic error. - Identify in 30 seconds whether a VPS is still running
docker-composev1 standalone — the binary deprecated since Docker Desktop 3.6 and removed from official packages. - Migrate to the
docker composev2 plugin with the exact two commands, then verify your existingdocker-compose.ymlfiles work without syntax changes. - Understand the nftables impact on your stacks: what continues to work (Docker networks), what can break (your scripts reading iptables), and the diagnostic command that settles it.
- Enable or defer the containerd store based on your migration timeline, with the exact configuration key and verification command.
- Evaluate the compatibility of Dockge, Portainer, and CasaOS App Store with v29, so you do not discover incompatibility during a client incident.
- Prepare your client VPS fleet so the next
apt upgradeis a planned event, not a late-night emergency.
Prerequisites before starting
This guide applies to any VPS running Ubuntu 22.04/24.04 or Debian 11/12 with Docker Engine installed from the official Docker Inc. repositories (not the distribution's docker.io package). You need SSH access as root or sudo. No service interruption is required for the diagnostic steps; migrating the compose plugin takes a few seconds during which docker compose commands are unavailable. Take a VPS snapshot before modifying /etc/docker/daemon.json if you enable the containerd store — restoring in case of issues takes less than five minutes with a VPS that offers on-demand snapshots.
Step 1 — Diagnose the API version
Check the client and daemon API versions
On each VPS to audit, run the following two commands:
docker version --format '{{.Client.APIVersion}}'
docker version --format '{{.Server.APIVersion}}'If the client version is below 1.44 and the daemon is running v29, you will get an error on the next commands. The 1.44 threshold is the minimum accepted by Docker Engine v29: a client compiled for 1.43 or earlier fails with Error response from daemon: client version 1.43 is too old. Minimum supported API version is 1.44, please upgrade your client.
If both lines show 1.44 or higher, your client is compatible. Proceed to the next step.
Detect the presence of docker-compose v1 standalone
The docker-compose command with a hyphen and the docker compose command without a hyphen are not the same thing. v1 is a standalone Python binary, v2 is a Go plugin integrated into the Docker CLI.
which docker-compose && docker-compose --versionIf the command returns a path in /usr/local/bin/ or /usr/bin/ with a version 1.x.x, you have the deprecated standalone binary. After the Docker v29 update, this binary returns docker-compose: command not found if the package was removed, or the API error described above if it is still present.
docker compose versionIf this command returns Docker Compose version v2.x.x, the v2 plugin is already present. Both can coexist temporarily, but the goal is to use only the v2 plugin.
Step 2 — Migrate from docker-compose v1 to the v2 plugin
Remove the v1 binary and install the plugin
apt remove docker-compose
apt install docker-compose-pluginOn a Debian or Ubuntu VPS using the official Docker Inc. repositories (download.docker.com), the docker-compose-plugin package is available without additional configuration. If apt remove docker-compose responds Package not found, the binary was installed manually: locate it with which docker-compose and delete the file.
Post-migration verification:
docker compose version
# Docker Compose version v2.36.0Verify syntax compatibility of your existing Compose files
The vast majority of docker-compose.yml files written for v1 work without modification with the v2 plugin. The only syntax breaks concern version: directives above "3.8" (ignored in v2, not blocking) and the --compatibility option (removed). Validate your existing files:
docker compose configThis command resolves environment variables, validates syntax, and displays the resolved configuration. An error-free output means your file is compatible.
If your team uses shell scripts with docker-compose (hyphen), add a compatibility alias in /etc/bash.bashrc on the VPS:
alias docker-compose='docker compose'This alias does not fix scripts that call docker-compose in absolute in a cron or systemd service — audit those separately.
Step 3 — Understand and adapt to the nftables network backend change
Verify that Docker networks still work
The good news: docker network works correctly with nftables. Inter-container traffic, NAT, and port exposure continue to work. What changes is the underlying tool that writes the rules.
docker network lsYour existing bridge networks are still listed. To verify that a container is receiving traffic on the expected port, test directly:
curl -s http://localhost:8080/healthIf the response is correct, the Docker data plane works regardless of the backend.
Diagnose the impact on your iptables scripts
The problem arises when a third-party script (monitoring, Ansible, UFW rules) inspects iptables to verify Docker rules are present:
iptables -L DOCKER 2>&1With the nftables backend, this chain is empty or absent. The script returns an error while Docker works perfectly. This is not a Docker failure — your audit tool is no longer looking in the right place.
To inspect the actual rules:
nft list ruleset | grep -A 20 'docker'If your monitoring scripts or Ansible playbooks check for Docker-specific iptables rules, adapt them to query nftables instead of inferring a failure.
Step 4 — Evaluate and enable the containerd image store
Check the current storage driver
docker info | grep 'Storage Driver'On a VPS updated to v29 without configuration changes, you typically get Storage Driver: overlay2. The containerd store is opt-in on v29 — it is not enabled automatically. It will become the default on v30.
Enable the containerd store (opt-in, recommended before v30)
To enable the containerd store on v29 and prepare the migration before it is imposed in v30, add the following key to /etc/docker/daemon.json:
{
"features": {
"containerd-snapshotter": true
}
}Restart the daemon:
systemctl restart dockerVerification:
docker info | grep 'Storage Driver'
# Storage Driver: overlayfsNote: existing images pulled under overlay2 remain available, but new layers are written in containerd format. If you need to revert, remove the key and restart — images in the new format will no longer be accessible without the containerd backend. This is why a snapshot before this step is recommended.
Third-party tool compatibility with Docker Engine v29
| Tool | v29 compatibility status | Recommended action |
|---|---|---|
| **Dockge** (up to 1.4.1 included) | Not compatible: the Dockge daemon calls API routes removed in v29. The panel no longer starts after the Docker update. | Update Dockge to version 1.4.2 or higher, which targets API v1.44. Check Dockge release notes before `apt upgrade` on a VPS hosting it. |
| **Portainer** (Community Edition < 2.21) | Partially compatible: the interface works, but standalone Docker environments may show errors on network views. Version 2.21 fixes the nftables calls. | Update Portainer via `docker pull portainer/portainer-ce:latest` then `docker compose up -d` before upgrading Docker Engine. |
| **CasaOS App Store** | Partial compatibility documented: deployed apps continue to work, but the app manager may report errors when inspecting images if the containerd store is enabled. No corrective version announced as of 2026-08. | Keep the containerd store opted out (v29 default) on CasaOS VPS until a corrective version. Test on a copy environment before any update. |
Test the migration on a snapshot before touching production
A VPS with root access and snapshots lets you validate each step of this migration without risk. Create a snapshot named before-docker-v29, perform the full migration, validate your stacks, then delete the snapshot. If something goes wrong mid-way, restoring brings the VPS back to its initial state in under five minutes. This is exactly the use case on-demand snapshots cover: testing a risky system update on an exact copy, not on a client's production server.
Troubleshooting — real errors and remedies
The following scenarios cover the majority of incidents observed during Docker v29 migrations on VPS fleets.
Common error scenarios
Error: `client version X.XX is too old. Minimum supported API version is 1.44`
Cause: the docker-compose v1 standalone binary is still present and attempts to communicate with the v29 daemon.
Remedy:
apt remove docker-compose
apt install docker-compose-plugin
docker compose versionIf the binary was installed manually (outside apt), find it:
which docker-compose
rm /usr/local/bin/docker-composeError: `docker-compose: command not found` after `apt upgrade`
Cause: the docker-compose package (v1) was removed during the update, and the v2 plugin was not installed.
Remedy:
apt install docker-compose-pluginThen verify that your scripts calling docker-compose (with hyphen) now use docker compose (without hyphen), or set the system alias.
Error: `iptables: No chain/target/match by that name` in a monitoring script
Cause: your script inspects the DOCKER chain in iptables, but Docker v29 with nftables no longer writes it there.
Remedy: replace the iptables check with an nftables check:
nft list ruleset | grep -c 'docker'If the count is above zero, Docker rules are present in nftables. Or use docker network inspect bridge to verify the data plane state directly from Docker, without depending on the network backend.
Dockge no longer starts after the update
Cause: Dockge 1.4.1 and earlier call API routes absent from Docker Engine v29.
Remedy:
cd /opt/dockge
docker compose pull
docker compose up -dIf the latest tag of the Dockge image is already at 1.4.2 or higher, this command suffices. Otherwise, edit your Dockge docker-compose.yml to point to the corrective version tag before relaunching.
Containers no longer respond on their ports after daemon restart
Cause: on the first restart of dockerd in nftables mode, NAT rules are rewritten in the correct backend, but some distributions have a conflict between the iptables-legacy service and nftables that delays rule setup.
Remedy:
systemctl stop docker
systemctl disable iptables
systemctl start dockerThen verify containers are restarted (docker compose up -d) and ports are exposed (`docker ps --format 'table {{.Names}}\t{{.Ports}}').
Preparing your fleet to avoid the next incident
A well-managed Docker v29 migration is not a one-off event — it is an opportunity to put in place the reflexes that prevent the next late-night emergency.
Lock the Docker version in apt. On client VPS, prevent Docker from updating automatically during unsupervised apt upgrade runs:
apt-mark hold docker-ce docker-ce-cli containerd.ioUnlock (apt-mark unhold) only when you are ready to migrate, after testing on a snapshot.
Automate fleet auditing. An Ansible playbook that checks the Docker API version on each VPS takes under an hour to write and gives you a dashboard of your fleet's exposure before each major Docker release.
Integrate Docker migration into your patch routine. The procedure described here — snapshot, API check, compose migration, nftables test, stack validation — documents as a runbook and replays at each major release.