What actually happens in the kernel during docker run -p
Every time a container is started with a published port, Docker silently rewrites the host's iptables rule set. This automation is convenient, but it regularly causes hard-to-trace conflicts once custom firewall rules or tools like firewalld enter the picture.
Table of Contents
- 1. Why Docker Touches iptables in the First Place
- 2. The Custom Chains Docker Creates
- 3. How a Port Mapping Turns Into a NAT Rule
- 4. Why Custom iptables Rules Can Collide
- 5. Systematic Debugging With iptables -L and -t nat -L
- 6. IP Forwarding and the Docker Bridge Working Together
- 7. The Move to nftables and What Changes
- 8. Real-World Example: A Container Is Unreachable From Outside
- 9. A Practical Checklist
- 10. Summary
- 11. FAQ
1. Why Docker Touches iptables in the First Place
Docker containers run by default in their own isolated network namespace with private IP addresses from a bridge network such as 172.17.0.0/16. For a container to be reachable from outside through a host port, or to reach the internet itself, traffic between that private address and the public host interface must be translated. The Linux kernel performs this translation through the Netfilter framework, driven by iptables rules.
Docker does not expect the user to manage this rule set manually. Instead, it writes its own rules into several iptables chains automatically, both when the daemon starts and on every docker run with a port mapping. This automation is exactly why container networking in Docker feels so effortless, but it is also the root cause of many conflicts once administrators try to maintain their own firewall rules alongside it.
2. The Custom Chains Docker Creates
On startup, the Docker daemon creates several dedicated chains, among them DOCKER, DOCKER-ISOLATION-STAGE-1, DOCKER-ISOLATION-STAGE-2, and DOCKER-USER. The DOCKER chain holds the NAT and forwarding rules for individually published ports, while the isolation stages prevent different Docker networks from communicating with each other unintentionally. These chains are wired into the standard FORWARD chain and the NAT table's DOCKER chain via jump rules.
Particularly relevant for custom adjustments is the DOCKER-USER chain, which Docker explicitly reserves for administrator rules and never overwrites on a daemon restart. Custom firewall rules meant to affect Docker-generated traffic almost always belong in this chain, not in the standard INPUT or FORWARD chain, since Docker regularly rewrites its own rules there.
# List all Docker-related chains
iptables -L -n --line-numbers | grep -A5 DOCKER
# Safely insert a custom rule into DOCKER-USER
# so it survives a Docker daemon restart
iptables -I DOCKER-USER -s 203.0.113.0/24 -j ACCEPT
iptables -I DOCKER-USER -j DROP
3. How a Port Mapping Turns Into a NAT Rule
When a container is started with -p 8080:80, Docker creates a rule in the NAT table's DOCKER chain that rewrites incoming traffic on host port 8080 to port 80 on the container's internal IP via destination NAT. In addition, a rule is added to the FORWARD chain that explicitly allows this redirected traffic, since a NAT rule alone does not permit packets to actually pass through the firewall.
This dual rule structure is a common stumbling block: looking only at the NAT table shows the translation but not whether the traffic is actually allowed through. Looking only at the filter table shows the forwarding decision but not where packets are actually rewritten to. A complete understanding always requires inspecting both tables together.
# Show the NAT rule for the port mapping
iptables -t nat -L DOCKER -n
# Example output:
# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080
# to:172.17.0.2:80
# Corresponding FORWARD rule
iptables -L FORWARD -n | grep 172.17.0.2
4. Why Custom iptables Rules Can Collide
A classic problem arises when an administrator sets a restrictive INPUT or FORWARD policy, assuming it also applies to Docker containers. Since Docker inserts its own jump rules near the top of the FORWARD chain, a later DROP rule often never fires because the traffic was already accepted earlier through the DOCKER chain. Conversely, a DROP rule placed too early can block all Docker traffic entirely, even though Docker's own rules are technically configured correctly.
Another widespread issue occurs when firewall management tools such as firewalld or ufw run alongside Docker. These tools rewrite the iptables rule set from their own configuration and can inadvertently remove Docker's rules or reorder them. After restarting such a tool, it is worth checking whether Docker containers are still reachable as expected.
5. Systematic Debugging With iptables -L and -t nat -L
The first step when facing networking issues with Docker containers is always to get a complete picture of both relevant tables: the filter table for forwarding decisions, and the NAT table for address translation. The command iptables -L -n -v additionally shows packet counters, which reveal whether a given rule has ever actually been hit, often far more useful during troubleshooting than the rule order alone.
For problems with a specific port mapping, it is worth filtering directly by the container's IP address instead of manually scanning the full output. This quickly reveals whether a NAT rule exists at all and whether the corresponding FORWARD rule actually permits the traffic, instead of checking both aspects separately without relating them to each other.
# Full overview with packet counters
iptables -L -n -v
iptables -t nat -L -n -v
# Filter specifically by a container IP
iptables -t nat -L -n | grep 172.17.0.2
iptables -L FORWARD -n -v | grep 172.17.0.2
# Watch live which rule is being hit
watch -n1 'iptables -L DOCKER -n -v'
6. IP Forwarding and the Docker Bridge Working Together
For iptables to forward packets between networks at all, the kernel parameter net.ipv4.ip_forward must be enabled. Docker typically sets this value automatically when the daemon starts, but in hardened environments or with manually managed sysctl configurations, it can inadvertently get disabled again, for example by a central configuration management system that resets the value on every reboot.
When ip_forward is disabled, even correctly configured NAT and FORWARD rules simply do not work, since the kernel will not forward packets between interfaces at all. This symptom is often misdiagnosed as a Docker networking bug, when the actual cause is a single disabled kernel setting that can be checked and fixed with one command.
# Check whether IP forwarding is active
sysctl net.ipv4.ip_forward
# Enable it if disabled (temporary)
sysctl -w net.ipv4.ip_forward=1
# Persist it in the configuration
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.d/99-docker.conf
7. The Move to nftables and What Changes
Modern Linux distributions increasingly rely on nftables as the successor to iptables, with the classic iptables command still working through a compatibility layer (iptables-nft). Docker has supported a native nftables mode since version 27, implementing the same NAT and forwarding concepts but using its own tables instead of the classic chains.
Anyone moving to a system with an nftables backend, or running Docker in nftables mode, needs to reach for nft list ruleset instead of iptables -L when debugging. The underlying logic of NAT translation and explicit forwarding permission stays identical, only the inspection tools differ.
# Check which backend is active
iptables --version
# View the rule set under nftables
nft list ruleset | grep -A20 docker
8. Real-World Example: A Container Is Unreachable From Outside
A typical support scenario: a container was started with -p 443:443, is reachable locally on the host via curl, but not from outside. The systematic debugging flow starts by checking whether the NAT rule exists at all, followed by checking the FORWARD chain, then ip_forward, and only last by considering external factors like cloud security groups or an upstream firewall.
In most cases, the cause is either firewall software installed afterward that overwrote Docker's rule set, or a restrictive DROP policy in DOCKER-USER that inadvertently blocks legitimate traffic too. Both causes can be narrowed down within a few minutes using the commands shown above, without needing to change the container configuration itself.
9. A Practical Checklist
For day-to-day operations, it pays off to follow a fixed sequence of checks instead of improvising from scratch for every networking problem. This sequence covers the most common causes, from the basic kernel setting through Docker's own chains to conflicts with external firewall software, and can be run through completely in just a few minutes.
The table below summarizes the key checkpoints, the corresponding commands, and typical causes of failure, so debugging does not have to start from zero the next time a networking problem comes up.
| Checkpoint | Command | Typical Cause of Failure | Priority |
|---|---|---|---|
| Is IP forwarding enabled? | sysctl net.ipv4.ip_forward |
Configuration management resets the value | High |
| Does the NAT rule exist? | iptables -t nat -L DOCKER -n |
Container started without a port mapping | High |
| Does the FORWARD rule exist? | iptables -L FORWARD -n -v |
External firewall tool overwrote the rules | High |
| Checked the DOCKER-USER chain? | iptables -L DOCKER-USER -n |
Custom DROP rule blocks traffic | Medium |
| Backend iptables or nftables? | iptables --version |
Wrong debugging tool being used | Low |
Mironsoft
Container infrastructure, CI pipelines and deployment automation
Docker setups that hold up across the team and in production?
We review existing Dockerfiles and Compose stacks for security gaps, bloated images and fragile build pipelines, then build a container infrastructure that builds fast, runs securely and stays understandable across the team.
Dockerfile Review
Systematically optimizing multi-stage builds, layer caching and image size.
Security Audit
Hardening container isolation, secrets handling and image scanning against real attack surfaces.
CI/CD Integration
Building build pipelines, registries and deployment strategies for reproducible releases.
10. Summary
iptables and Docker: The Essentials at a Glance
Automation
Docker writes its own NAT and FORWARD rules into iptables for every published port.
DOCKER-USER
The only chain reserved for custom, persistent firewall rules.
Collisions
Firewall tools like firewalld can overwrite or reorder Docker's rules.
Debugging
Always check the NAT and FORWARD tables together, plus the ip_forward status.