Docker Is Bypassing UFW — Published Ports Are Open to the World

docker run -p 5432:5432 punches through UFW by writing its own iptables rules. Your database may be publicly reachable even though UFW 'denies' it.

What you'll see

Root causes

Docker writes NAT rules ahead of UFW

The DOCKER chain processes published ports before UFW's INPUT rules ever see the packet.

Fix it

  1. Never publish to all interfaces; bind to loopback or the internal net
    docker run -p 127.0.0.1:5432:5432 postgres  # or omit -p and use a network
  2. If you must expose, control it in the DOCKER-USER chain
    iptables -I DOCKER-USER -p tcp --dport 5432 ! -s 10.0.0.0/8 -j DROP
  3. Audit what is actually exposed right now
    ss -tlnp | awk '$4 !~ /127.0.0.1|\[::1\]/'

Field note

Assume every -p is public until proven otherwise. Cloud firewalls (security groups, Cloudflare Access) are the reliable boundary — host firewalls do not reliably constrain Docker.

Common questions

Why do my published Docker ports bypass UFW rules?

Docker writes its own iptables NAT rules (DOCKER chain) ahead of UFW's chains: published ports are exposed before UFW INPUT rules evaluate. It's a rule-ordering reality, not a UFW bug — UFW's deny does not apply to Docker's forwarded traffic.

How do I protect published ports then?

Bind them to the intended interface: -p 127.0.0.1:5432:5432 for local-only, or the LAN IP for internal services. Optionally use DOCKER-USER chain rules for explicit allowlists — the bind approach is simpler and self-documenting.

Ship it right the first time

Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.

Browse the template store →

One-time. Yours to modify. Instant download from the NinjaOps template store.