UFW (Uncomplicated Firewall) is a frontend for iptables. It's not the most powerful tool, but it's fast to configure, easy to audit, and doesn't require memorizing iptables syntax at 2am when something breaks.
Quick answer: UFW (Uncomplicated Firewall) is a simple front-end to iptables. Set default deny incoming and default allow outgoing, allow SSH before enabling (ufw allow OpenSSH), then ufw enable. Add services with ufw allow 80,443/tcp. Note UFW does not filter Docker's published ports.
The main danger: enabling UFW before allowing SSH. You lock yourself out and need console access to fix it. The order below avoids that.
Check if UFW is installed
sudo ufw status
Should be inactive on a fresh Ubuntu server. If it's already active and you don't know what rules are set, check before touching anything:
sudo ufw status verbose
The safe enable sequence
# 1. Allow SSH first — before enabling anything
sudo ufw allow ssh
# 2. Now enable
sudo ufw enable
That's it. Don't enable first and add SSH second. I've seen people do it, I've done it myself once — you have about 30 seconds before the existing session times out and you're locked out.
If you're on a non-standard SSH port:
sudo ufw allow 2222/tcp
Default policies
UFW starts with defaults: deny all incoming, allow all outgoing. That's the right starting point. Confirm:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Common rules
# Web server
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Or use the Nginx profile (if installed):
sudo ufw allow 'Nginx Full'
# Specific port
sudo ufw allow 8080/tcp
# Specific IP only (e.g. office access to a management port)
sudo ufw allow from 203.0.113.42 to any port 8080
# Specific subnet
sudo ufw allow from 10.0.0.0/24
# Allow a port range
sudo ufw allow 6000:6100/tcp
Application profiles
UFW has pre-defined profiles for common services. See what's available:
sudo ufw app list
Common ones: OpenSSH, Nginx Full, Nginx HTTP, Nginx HTTPS. Using profiles is cleaner than raw ports because the name documents the intent when you read the rules back later.
View and manage rules
# List rules with numbers
sudo ufw status numbered
# Delete by number
sudo ufw delete 3
# Delete by rule definition
sudo ufw delete allow 8080/tcp
Deleting by number is more reliable when you have multiple similar rules. Always list numbered before deleting.
UFW and Docker: the problem
Docker bypasses UFW by manipulating iptables directly. If you run docker run -p 8080:80 nginx, port 8080 is publicly accessible even if UFW has no rule for it.
This is a known Docker behavior that surprises everyone the first time. Three options:
Option 1 — Only bind to localhost in Docker (my preference for containers behind Nginx):
docker run -p 127.0.0.1:8080:80 nginx
The container is only accessible from the host itself. Nginx proxies to it. UFW is irrelevant because nothing external reaches Docker directly.
Option 2 — Use Docker's built-in network controls: define networks in compose files and don't expose ports that don't need to be public.
Option 3 — Disable Docker's iptables manipulation (complex, breaks container networking if you're not careful): add "iptables": false to /etc/docker/daemon.json. Only do this if you know what you're replacing it with.
Rate limiting with UFW
UFW has a built-in rate limit rule that bans IPs making more than 6 connections in 30 seconds:
sudo ufw limit ssh
It's a blunt instrument compared to fail2ban, but if you're not running fail2ban it's better than nothing. If you have fail2ban already set up, you don't need this.
Logging
# Enable logging
sudo ufw logging on
# Check what's being blocked
sudo tail -f /var/log/ufw.log
Log levels: low (blocked only), medium, high, full. Default is low. I keep it at low — medium and above on a busy server generates a lot of noise.
Reset everything
If you've made a mess and want to start over:
sudo ufw reset
Disables UFW and deletes all rules. Then follow the safe enable sequence above.
Quick reference: my standard setup for a web server
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status verbose
Six commands. Done.
FAQ
How do I set up a basic UFW firewall?
Run ufw default deny incoming, ufw default allow outgoing, ufw allow OpenSSH, then ufw enable. Add each service port you need afterward.
Will enabling UFW disconnect my SSH session?
Only if you enable it before allowing SSH — the default-deny will cut you off. Always run ufw allow OpenSSH first, then ufw enable.
Why doesn't UFW block my Docker container ports?
Docker writes its own iptables rules and bypasses UFW entirely. Published container ports are reachable even when UFW says deny. Bind containers to 127.0.0.1 or apply the ufw-docker fix.
How do I rate-limit SSH with UFW?
Use ufw limit OpenSSH. It blocks any source IP that makes six or more connection attempts within 30 seconds — a cheap brute-force deterrent.
How do I view and delete rules?
List with ufw status verbose, or ufw status numbered to see index numbers, then delete a specific rule with ufw delete <number>.