NAT sounds academic until your Docker port mapping or reverse proxy breaks. SNAT and DNAT are just “who gets rewritten” on the packet.
DNAT — destination NAT (incoming)
DNAT changes the destination IP/port of inbound traffic. Your VPS public IP :443 hits Nginx or Traefik; DNAT (or Docker’s publish rules) forwards to a container on `172.17.0.2:3000`. The client never talks to the private IP directly.
- Docker `-p 8080:80` → DNAT from host 8080 to container 80
- Reverse proxy virtual hosts → DNAT-like behavior at application layer
- Home lab: port forward on router → DNAT to your NAS
SNAT — source NAT (outgoing)
SNAT rewrites the source address of packets leaving a private network. Containers with only internal IPs reach the internet through the host’s public IP — the host masquerades (MASQUERADE) outbound traffic. Replies come back to the host, which unmangles and forwards to the container.
Where you see both on one VPS
Request: Internet → DNAT → Nuxt container. Container fetches API on same host: may hairpin or go out and SNAT back. Webhook callbacks from container to Stripe: SNAT applies. Misconfigured SNAT is why some setups “work locally in container network” but fail for external OAuth callbacks.
- Check `iptables -t nat -L` when debugging publish ports
- Prefer one reverse proxy (Traefik/Caddy) over dozens of published ports
- Document public URL vs internal service name for your team
