I'm trying to simulate an AWS-style NAT gateway with Docker Compose. The nat-gateway container is attached to both a private and a public bridge network, while the api container should exist only on the private network and send all internet-bound traffic through the nat-gateway. I enabled IPv4 forwarding and added `iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE`, but packet captures still show `172.19.0.2` as the source address on outbound traffic. My current Compose configuration uses `network_mode: "service:nat-gateway"` for the API. What is the correct network layout and routing setup for this kind of NAT simulation?
2 Answers
`network_mode: "service:nat-gateway"` makes both containers share the exact same network namespace. It does not place the API behind the gateway as a separate machine. They use the same interfaces and routing table, so there is no routing hop between them where the gateway’s POSTROUTING rule would apply. Remove `network_mode: "service:nat-gateway"` and attach the API only to `private-vpc`.
For a closer AWS NAT-gateway simulation, give the API its own network namespace on the private bridge and attach the NAT container to both networks. Enable forwarding in the NAT container, then make the NAT container’s private-side address the API’s default gateway. You may need `NET_ADMIN` and `iproute2` in the API image to replace its default route. On the NAT container, masquerade traffic leaving the public-side interface, but verify the actual interface name with `ip addr` rather than assuming `eth0` or `eth1`.

The important distinction is that the API must be a separate namespace. Sharing the NAT container’s namespace bypasses the routing path entirely, so changing MASQUERADE rules alone cannot create the missing hop.