01The Docker Firewall Problem
If you're using UFW and Docker together, your containers may be exposed to the internet right now. Check your setup!
02Checking If You're Exposed
1# Check what's listening on all interfaces2sudo ss -tlnp | grep docker34# Check iptables rules Docker has added5sudo iptables -L DOCKER -n -v67# From another machine or phone (different network), try to access:8# curl http://your-public-ip:exposed-port910# Check UFW status11sudo ufw status verbose1213# Look for exposed ports in compose files14grep -r "ports:" docker-compose.yml1516# Ports bound to 0.0.0.0 are potentially exposed17docker ps --format '{{.Ports}}' | grep 0.0.0.0Use a tool like nmap from outside your network to verify which ports are actually accessible.
03Solution 1: Bind to Localhost
1services: 2 # WRONG - exposed to everyone3 database-bad: 4 image: postgres:165 ports: 6 - "5432:5432" # Binds to 0.0.0.078 # CORRECT - only accessible from host9 database-good: 10 image: postgres:1611 ports: 12 - "127.0.0.1:5432:5432" # Binds to localhost only1314 # Use reverse proxy for web services15 traefik: 16 image: traefik:v3.017 ports: 18 - "80:80" # Only these are intentionally public19 - "443:443"2021 webapp: 22 image: myapp:latest23 # No ports exposed! Accessed via Traefik24 labels: 25 - "traefik.enable=true"26 - "traefik.http.routers.app.rule=Host(`app.example.com`)"This is the recommended approach. Only expose ports that truly need to be public (usually just 80 and 443 for your reverse proxy).
04Solution 2: The DOCKER-USER Chain
1# The DOCKER-USER chain is processed BEFORE Docker's rules2# Add your filtering rules here34# Allow established connections5sudo iptables -I DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN67# Allow from local network8sudo iptables -I DOCKER-USER -s 192.168.1.0/24 -j RETURN910# Allow from specific IPs11sudo iptables -I DOCKER-USER -s 1.2.3.4 -j RETURN1213# Drop everything else going to Docker containers14sudo iptables -A DOCKER-USER -j DROP1516# Make rules persistent17sudo apt install iptables-persistent18sudo netfilter-persistent save1920# Or use a script in /etc/rc.localGet the order right! RETURN rules must come before DROP. Test carefully to avoid locking yourself out.
05Solution 3: UFW + Docker Fix
1# Option A: ufw-docker (recommended)2# Install the helper script3sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker4sudo chmod +x /usr/local/bin/ufw-docker56# Install the UFW rules7sudo ufw-docker install89# Now manage Docker ports through ufw-docker10sudo ufw-docker allow webapp 80/tcp11sudo ufw-docker allow webapp 443/tcp1213# List Docker rules14sudo ufw-docker status1516# Option B: Disable Docker's iptables (not recommended)17# /etc/docker/daemon.json18{19 "iptables": false20}21# WARNING: This breaks container networking!The ufw-docker script provides the best balance of UFW integration and working container networking.
06Using Docker Networks for Isolation
1services: 2 # Reverse proxy - the only thing exposed3 traefik: 4 image: traefik:v3.05 ports: 6 - "80:80"7 - "443:443"8 networks: 9 - frontend10 volumes: 11 - /var/run/docker.sock:/var/run/docker.sock:ro1213 # Web app - no ports, accessed via Traefik14 webapp: 15 image: myapp:latest16 networks: 17 - frontend18 - backend19 labels: 20 - "traefik.enable=true"21 - "traefik.http.routers.app.rule=Host(`app.example.com`)"2223 # Database - completely isolated24 database: 25 image: postgres:1626 # NO PORTS - only webapp can access via backend network27 networks: 28 - backend29 volumes: 30 - db_data:/var/lib/postgresql/data3132networks: 33 frontend: 34 backend: 35 internal: true # No external access at all3637volumes: 38 db_data: 07Using Cloud Provider Firewalls
1# AWS Security Groups, Azure NSG, GCP Firewall Rules2# These filter traffic before it reaches your server34# Example: Only allow SSH and HTTPS5# Inbound rules:6# - Port 22/tcp from YOUR_IP (SSH)7# - Port 80/tcp from 0.0.0.0/0 (HTTP for redirect)8# - Port 443/tcp from 0.0.0.0/0 (HTTPS)9# - All other inbound: DENY1011# For home servers behind a router:12# Only port forward 80 and 443 to your Docker host13# Keep all other ports closed1415# Verify external access (from different network)16nmap -Pn your-public-ipCloud firewalls are the most reliable solution. Docker can't bypass rules that are enforced before traffic reaches your server.