Docker’s iptables integration is a bit of a black box, but it’s fundamental to how containers talk to each other and the outside world. The surprising truth is that Docker rewrites your iptables rules to manage its own network namespaces and port forwarding.

Let’s see this in action. Imagine a simple two-container setup: a web server in web-app and a database in db.

# Start the database container
docker run -d --name db -p 5432:5432 postgres:latest

# Start the web application container
docker run -d --name web-app -p 8080:80 my-custom-web-app:latest

Now, let’s peek at the iptables rules. Docker typically creates a DOCKER chain.

sudo iptables -t nat -L DOCKER -n -v

You’ll see entries like this (simplified):

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* docker filter */ ADDRTYPE match src_type !INADDR_ANY
    0     0 DNAT       tcp  --  *      eth0    0.0.0.0/0            172.17.0.2           tcp dpt:5432 to:172.17.0.2:5432
    0     0 DNAT       tcp  --  *      eth0    0.0.0.0/0            172.17.0.3           tcp dpt:8080 to:172.17.0.3:80

This shows Docker’s Network Address Translation (NAT) rules. The DNAT (Destination Network Address Translation) rules are key. When traffic hits your host’s port 5432 (for the db container) or 8080 (for web-app), Docker intercepts it and rewrites the destination IP and port to point to the correct container’s internal IP address and port.

Docker creates a bridge network (usually docker0 by default) with a specific subnet (e.g., 172.17.0.0/16). Each container gets an IP address within this subnet. The iptables rules, managed by Docker, ensure that traffic directed to the host’s published ports is correctly forwarded to these container IPs.

The core components you control are:

  • Port Mapping (-p flag): This is how you expose container ports to the host. Docker automatically generates the iptables NAT rules for these.
  • Network Drivers: The default bridge driver is what uses iptables. Other drivers like host or overlay (for Swarm/Kubernetes) behave differently.
  • iptables Chains: Docker inserts its rules into specific iptables chains, primarily DOCKER and DOCKER-ISOLATION-STAGE-X. Understanding these chains helps when you need to inject custom rules.

When you run a container with -p 8080:80, Docker’s iptables manager adds a rule to the DOCKER chain in the nat table. This rule looks for TCP traffic destined for the host’s IP address on port 8080 (0.0.0.0/0 to HOST_IP:8080). If it matches, it performs a DNAT operation, changing the destination to the container’s internal IP and port (e.g., 172.17.0.3:80). This is why you can access your containerized web app by going to http://localhost:8080 on your host machine.

A common pitfall is trying to manage Docker’s iptables rules directly without understanding Docker’s lifecycle. If you add a rule and then restart or remove a container, Docker might reorder or even delete your custom rules, breaking your intended network configuration. To manage custom iptables rules that persist across container restarts, you typically need to place them in scripts that are executed after Docker has initialized its own rules, or use tools designed for persistent iptables management on your host OS.

The iptables-restore command is your friend here, but it needs to be used carefully. If you save your iptables state with iptables-save > rules.v4 and then try to restore it with iptables-restore < rules.v4 after Docker has started, Docker’s subsequent container operations will likely overwrite your restored rules. The correct approach involves understanding Docker’s default network setup and strategically inserting your rules into the appropriate chains (e.g., DOCKER-USER) that Docker respects for user-defined modifications without interfering with its own internal management.

The next challenge is often understanding how to make containers communicate with each other securely using Docker’s networking, beyond simple port mapping.

Want structured learning?

Take the full Iptables course →