How to Set Up UFW Firewall on Linux VPS (Fix Docker Bypass)

Verdict Cepat / TL;DR: Setting up Uncomplicated Firewall (UFW) on your Linux VPS protects your open ports from port scanners and unauthorized SSH login attempts. However, Docker silently bypasses default UFW rules by modifying raw iptables chains directly. To build a secure production server, you must default-deny incoming traffic, allow essential ports (22, 80, 443), and apply iptables-nft fixes or ufw-docker rules to stop containers from exposing internal ports directly to the public internet.

Why Every Linux VPS Needs a Host-Based Firewall

When you launch a fresh VPS on platforms like DigitalOcean, Linode, or AWS EC2, your server is assigned a public IPv4 address that gets pinged by automated botnets within minutes. Leaving every port wide open means any internal service listening on localhost or 0.0.0.0 (like Redis, PostgreSQL, or an unauthenticated admin dashboard) becomes immediately accessible to anyone scanning your IP.

While cloud providers offer network security groups, a host-based firewall on the server itself provides an essential defense-in-depth layer. Uncomplicated Firewall (UFW) serves as the standard user-friendly wrapper for iptables and nftables on Ubuntu and Debian distributions. It replaces complex networking rules with clear, readable commands.

However, running UFW on modern servers involves a critical pitfall: Docker’s default networking behavior silently bypasses UFW rules. When Docker binds container ports, it inserts rules into iptables before UFW’s filter chain executes. This guide breaks down step-by-step UFW configuration, port hardening, and the exact fix for the hidden Docker security loophole.

Saya have spent years managing cloud infrastructure, and I can tell you that an unconfigured firewall is one of the easiest vulnerabilities to exploit. Saya always enforce strict default-deny policies on every fresh instance. Saya recommend treating host firewall setup as mandatory before installing any production software stack.

Prerequisites and Server Requirements

Before configuring UFW firewall rules, confirm that your environment matches these requirements:

  • Operating System: Ubuntu 22.04 LTS, Ubuntu 24.04 LTS, or Debian 12 (Bookworm).
  • User Permissions: A non-root user with sudo privileges or direct root SSH access.
  • Terminal Access: Active SSH session to your server command line.
  • Open SSH Port: Ensure you know your custom SSH port if you do not use standard port 22.

Step 1: Check UFW Status and Set Default Policies

By default, UFW is installed on Ubuntu servers but remains inactive. Check its current operational status by running:

sudo ufw status verbose

If UFW reports Status: inactive, do not enable it yet! Enabling UFW before allowing your SSH port will immediately lock you out of your remote server session.

First, configure the baseline default policies. A secure firewall configuration denies all incoming connection attempts while allowing all outgoing traffic from your server:

sudo ufw default deny incoming
sudo ufw default allow outgoing

These two commands guarantee that any service or port you do not explicitly open will block incoming requests by default.

Step 2: Allow Essential Ports (SSH, HTTP, HTTPS)

Now, explicitly whitelist the ports required for server administration and web hosting. To maintain your active SSH connection, allow port 22 (or your custom SSH port) before turning the firewall on:

sudo ufw allow 22/tcp

If your SSH daemon uses a custom port such as 2222, allow that port instead:

sudo ufw allow 2222/tcp

Next, allow standard HTTP and HTTPS web traffic for web servers like Nginx, Apache, or reverse proxies like Nginx Proxy Manager:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Alternatively, you can use application profile names registered with UFW:

sudo ufw allow "Nginx Full"
sudo ufw allow OpenSSH

Step 3: Enable UFW and Verify Active Rules

With SSH and web ports safely allowed in the rule queue, turn on UFW:

sudo ufw enable

The terminal will prompt you with a warning that command evaluation may disrupt existing SSH connections. Type y and press Enter. To verify your rules are active, inspect the numbered status list:

sudo ufw status numbered

You will see output structured like this:

Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 22/tcp                     ALLOW IN    Anywhere                  
[ 2] 80/tcp                     ALLOW IN    Anywhere                  
[ 3] 443/tcp                    ALLOW IN    Anywhere                  
[ 4] 22/tcp (v6)                ALLOW IN    Anywhere (v6)             
[ 5] 80/tcp (v6)                ALLOW IN    Anywhere (v6)             
[ 6] 443/tcp (v6)               ALLOW IN    Anywhere (v6)

Step 4: Advanced UFW Configuration (IP Whitelisting & Ranges)

For administrative services such as database engines, Redis, or internal web dashboards, opening ports to the public internet is risky. UFW lets you restrict access to specific static IP addresses or subnet blocks.

Allowing Access from a Specific IP Address

To restrict database access (e.g., PostgreSQL port 5432) so that only your home office IP (e.g., 203.0.113.45) can connect, run:

sudo ufw allow from 203.0.113.45 to any port 5432 proto tcp

Allowing Subnet Ranges

If you operate a cluster of VPS instances on a private VPC network (e.g., 10.136.0.0/16), grant full internal communication across all ports with one rule:

sudo ufw allow from 10.136.0.0/16

Deleting Unwanted UFW Rules

If you made a typo or no longer need a port open, view the rule numbers with sudo ufw status numbered and delete the target rule by index:

sudo ufw delete 3

The Hidden Danger: How Docker Bypasses UFW Rules

Many system administrators assume that running sudo ufw default deny incoming keeps all unlisted ports safe. However, when you launch a Docker container with published ports, such as docker run -d -p 8080:80 nginx, Docker modifies Linux iptables directly in the PREROUTING chain of the nat table.

Because Docker places its routing rules before the ufw-user-input chain, external traffic to port 8080 hits the Docker container directly, completely ignoring your UFW default deny policy. Even if UFW shows port 8080 as blocked or unlisted, port scanners can still access your container service worldwide.

Linux server rack hardware representing network security and firewall configuration
Server infrastructure requires host-based firewall rules to protect exposed container ports. (Source: Unsplash)

Fixing the Docker UFW Bypass Loophole

To restore UFW control over container traffic without breaking internal Docker bridge networking, choose one of these two industry-standard fixes:

Solution A: Bind Docker Containers to Localhost

If your containers are routed through a local reverse proxy (like Nginx, Caddy, or Nginx Proxy Manager), do not publish container ports to 0.0.0.0. Instead, bind them explicitly to the local loopback interface 127.0.0.1 in your CLI commands or docker-compose.yml files:

ports:
  - "127.0.0.1:8080:80"

Because the container only listens on loopback, external users on public interfaces cannot reach it, while your local Nginx proxy can route requests safely.

Solution B: Implement ufw-docker Security Rules

If you need Docker containers exposed selectively on public interfaces, use the open-source ufw-docker patch. This utility modifies /etc/ufw/after.rules so UFW evaluates incoming packets before forwarding them to Docker’s DOCKER-USER iptables chain.

Append the following rule block to the end of /etc/ufw/after.rules:

# BEGIN UFW AND DOCKER FIX
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --dport 53 -j RETURN
-A DOCKER-USER -p tcp -m tcp --dport 53 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER FIX

After editing the file, reload UFW to apply the patch:

sudo ufw reload

With this patch active, you can allow public access to a specific Docker container (e.g., container web-app on port 80) using simple UFW syntax:

sudo ufw route allow proto tcp from any to any port 80

UFW vs. Raw iptables vs. Cloud Security Groups

Understanding where UFW fits in your overall architecture helps you design better server defenses. The table below compares host firewalls with network security groups.

FeatureUFW (Host Firewall)Raw iptables / nftablesCloud Security Groups
LocationInside OS kernelInside OS kernelCloud hypervisor / network boundary
Ease of UseVery High (Simple CLI)Low (Complex syntax)High (Web GUI / Terraform)
Docker IntegrationBypassed by default (Requires patch)Directly managed by Docker daemonUnaffected by Docker (Operates outside OS)
Resource UsageNegligibleNegligibleZero host CPU/RAM usage
Performance ImpactSub-millisecond filteringSub-millisecond filteringFiltered before reaching host NIC
Best PurposeInternal host protection & rule simplicityCustom packet manipulation & NATFirst line of network perimeter defense

Troubleshooting Common UFW Issues

If you encounter unexpected connectivity loss or blocked services, work through these diagnostic checks:

  • SSH Connection Blocked: If you accidentally enabled UFW without allowing port 22, log into your cloud provider’s web console (e.g., DigitalOcean VNC Console or AWS Serial Console) and run sudo ufw allow 22/tcp && sudo ufw reload.
  • Web Application 502/Connection Refused: Verify whether your app is bound to 127.0.0.1 or 0.0.0.0 using sudo netstat -tulpn | grep LISTEN. If UFW blocks inter-container communication, check if internal Docker bridge interfaces are allowed.
  • Inspecting Blocked Log Messages: Enable UFW logging with sudo ufw logging on and monitor incoming blocked packets in real time with sudo tail -f /var/log/ufw.log.

CTA / Summary

Configuring UFW on a Linux VPS provides an instant security boost against unauthorized scanning and exploit attempts. By setting default deny rules, whitelisting administrative IPs, and fixing Docker’s default iptables bypass, your server remains locked down and production-ready.

Building out a complete VPS stack? Check out our step-by-step tutorial on how to set up Nginx Proxy Manager on a VPS or read our hands-on guide on setting up Docker on a VPS with Portainer to streamline your container deployment workflow. You can also explore vendor documentation at Ubuntu Official Firewall Documentation, Docker Firewall Docs, DigitalOcean UFW Guide, and Debian Firewall Reference.

Irfan is a Creative Tech Strategist and the founder of Grafisify. He spends his days testing the latest AI design tools and breaking down complex tech into actionable guides for creators. When he’s not writing, he’s experimenting with generative art or optimizing digital workflows.

Leave a Reply

Your email address will not be published. Required fields are marked *

You might also like
How to Self-Host a Password Manager with Vaultwarden

How to Self-Host a Password Manager with Vaultwarden

How to Back Up a VPS with Restic and Backblaze B2

How to Back Up a VPS with Restic and Backblaze B2

How to Set Up WireGuard VPN on a Linux VPS

How to Set Up WireGuard VPN on a Linux VPS

How to Set Up Uptime Kuma on a VPS

How to Set Up Uptime Kuma on a VPS

Linux VPS Security Rules: 7 Steps to Harden SSH Access

Linux VPS Security Rules: 7 Steps to Harden SSH Access

SSH Hardening for Linux VPS: 7 Security Rules

SSH Hardening for Linux VPS: 7 Security Rules