The Error
You restart your server, bring your containers back up, and get hit with this:
Error response from daemon: driver failed programming external connectivity on endpoint mycontainer: iptables: No chain/target/match by that name.
Everything was working fine before the reboot. The containers haven't changed. Yet Docker refuses to start them. It's maddening because nothing on the surface looks broken.
What's Actually Happening
Docker manages container networking by injecting rules into iptables โ specifically into chains like DOCKER, DOCKER-USER, and DOCKER-ISOLATION-STAGE-1. Docker creates these chains when the daemon starts.
After a reboot, if something reloads or resets the firewall rules after Docker starts, those chains get wiped. Docker's rules are gone, but Docker doesn't know that. Start a container, and Docker tries to append rules to chains that no longer exist โ hence "No chain/target/match by that name."
Usual suspects:
firewalldorufwreloading after Docker starts and flushing iptables- A custom
iptables -Foriptables --flushscript running at boot - A systemd ordering race: Docker starts before the firewall is ready, firewall then resets everything
- Switching between
iptables-legacyandiptables-nftโ very common on Debian/Ubuntu 20.04+
Fix 1: Restart the Docker Daemon (Quick Fix)
Fastest recovery right now: restart Docker. This forces it to recreate its iptables chains from scratch.
sudo systemctl restart docker
Then start your container:
docker start mycontainer
# or
docker compose up -d
Works immediately in most cases. But the error comes back on the next reboot if the root cause isn't addressed โ so keep reading.
Fix 2: Check the iptables Backend (Ubuntu/Debian)
On Ubuntu 20.04+ and Debian 10+, the system may be using iptables-nft (nftables backend) while Docker expects iptables-legacy. When they don't match, chain lookups fail silently during Docker startup.
Check which backend is active:
sudo update-alternatives --display iptables
If it shows iptables-nft as the current choice, switch to legacy:
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
Restart Docker:
sudo systemctl restart docker
This alone resolves the error on the majority of modern Debian/Ubuntu machines. Try this before anything else on those systems.
Fix 3: Fix the systemd Service Order (firewalld)
Running firewalld? That's likely your culprit. Firewalld reloads its own ruleset during boot โ and if that reload happens after Docker has already set up its chains, firewalld wipes them.
Confirm firewalld is active:
sudo systemctl status firewalld
sudo firewall-cmd --list-all
Tell systemd Docker must wait for firewalld to finish before starting:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/after-firewalld.conf <<EOF
[Unit]
After=firewalld.service
EOF
Apply the change:
sudo systemctl daemon-reload
sudo systemctl restart docker
On dev servers where you don't actually need firewalld, just turn it off:
sudo systemctl disable --now firewalld
Fix 4: Stop iptables Flush Scripts from Running After Docker
Got a custom script that flushes iptables rules โ something in /etc/rc.local or a dedicated systemd unit? Check its boot timing relative to Docker.
Flush all chains manually, then let Docker rebuild:
# Clear all iptables rules
sudo iptables -F
sudo iptables -X
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo iptables -t mangle -F
sudo iptables -t mangle -X
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
# Rebuild Docker's chains
sudo systemctl restart docker
Need to keep the flush script? Add a systemctl restart docker at the end of it. Or wire up the systemd ordering so Docker always starts last.
Verification
After applying a fix, confirm Docker's chains exist:
sudo iptables -L DOCKER --line-numbers
You should see something like:
Chain DOCKER (1 references)
num target prot opt source destination
Getting iptables: No chain/target/match by that name here means Docker's chains still aren't up. Restart the daemon and check again.
Verify the container starts cleanly:
docker start mycontainer
docker ps # should show container as Up
Do a full reboot test to confirm the fix survives:
sudo reboot
# After reconnecting:
docker ps -a # check container status
journalctl -u docker -n 50 # scan Docker logs for errors
Prevention
A few things worth locking in so this doesn't resurface:
- Pin the iptables backend. Switching to
iptables-legacysticks across reboots โ but double-check after major OS upgrades.apt upgradeon Ubuntu can silently flip it back to nft. - Use
--iptables=falseonly if you know what you're doing. Set"iptables": falsein/etc/docker/daemon.jsonto take full control โ but that means you own every NAT and forwarding rule manually. One missed rule and containers lose internet access. - Visualize your boot order. Run
systemd-analyze plot > boot.svgand open the SVG in a browser. You'll see exactly where Docker and firewalld land on the timeline โ race conditions become obvious at a glance. - Plan your Docker subnets. When running multiple Docker networks with custom subnets, CIDR conflicts with the host network cause a different but equally confusing class of errors. The Subnet Calculator on ToolCraft is handy for quickly checking whether a bridge subnet clashes with existing routes before assigning it.
Quick Reference
# Most common fix sequence:
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo systemctl restart docker
# Verify Docker chains are up:
sudo iptables -L DOCKER
# Check Docker logs if still failing:
journalctl -u docker -n 100 --no-pager

