A properly configured firewall is the first line of defense for any server exposed to the internet. Ubuntu includes UFW (Uncomplicated Firewall) as its default firewall management tool, providing a straightforward interface on top of the powerful but complex iptables framework. This guide covers everything you need to know to configure UFW for production Ubuntu servers, from basic rule management to advanced configurations like rate limiting, application profiles, and subnet-based access control.
Prerequisites
Before you begin, make sure you have:
- Ubuntu Server 20.04, 22.04, or 24.04 (Desktop editions also work)
- Terminal access with sudo privileges
- SSH access to the server (if configuring remotely)
- Basic understanding of TCP/UDP ports and networking concepts
What Is UFW?
UFW stands for Uncomplicated Firewall. It was created by the Ubuntu team to simplify the process of configuring iptables, the built-in Linux firewall framework. While iptables is extremely powerful, its syntax is verbose and error-prone for routine operations. UFW wraps iptables with a human-readable command interface.
Key characteristics of UFW:
- Stateful firewall — it tracks connection states and automatically allows return traffic for established connections
- Application profiles — pre-configured rule sets for common services
- IPv4 and IPv6 support — dual-stack by default
- Logging — configurable log levels for security monitoring
- Rate limiting — built-in protection against brute-force attacks
Installing UFW
UFW is pre-installed on most Ubuntu distributions. Verify the installation:
sudo apt update
sudo apt install ufw
Check the current status:
sudo ufw status
If UFW has never been configured, the output will show:
Status: inactive
Important: Do not enable UFW until you have configured your SSH access rule. Enabling the firewall without allowing SSH will lock you out of a remote server.
Setting Default Policies
Before adding any rules, establish the default policies. The recommended security posture is to deny all incoming traffic and allow all outgoing traffic:
sudo ufw default deny incoming
sudo ufw default allow outgoing
This means:
- Incoming: All unsolicited connections from outside are blocked unless you create a specific rule to allow them
- Outgoing: Your server can initiate connections to the outside world freely (for updates, DNS lookups, API calls, etc.)
If you want a more restrictive setup, you can also deny outgoing traffic by default:
sudo ufw default deny outgoing
However, this requires you to explicitly allow every outgoing connection (DNS, NTP, package updates), which adds significant management overhead.
Allowing SSH Before Enabling UFW
This is the most critical step when configuring UFW on a remote server. Always allow SSH first:
sudo ufw allow ssh
This is equivalent to:
sudo ufw allow 22/tcp
If your SSH server runs on a non-standard port (for example, 2222):
sudo ufw allow 2222/tcp
Enabling UFW
Once SSH is allowed, enable the firewall:
sudo ufw enable
UFW will display a warning:
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
Your existing SSH session will not be interrupted because UFW maintains stateful tracking for established connections.
To verify:
sudo ufw status verbose
Expected output:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
Common Firewall Rules
Allow a Specific Port
# Allow HTTP
sudo ufw allow 80/tcp
# Allow HTTPS
sudo ufw allow 443/tcp
# Allow a custom application port
sudo ufw allow 8080/tcp
# Allow a UDP port (e.g., for WireGuard VPN)
sudo ufw allow 51820/udp
Allow a Port Range
# Allow ports 6000 through 6007 for TCP
sudo ufw allow 6000:6007/tcp
# Allow ports 6000 through 6007 for UDP
sudo ufw allow 6000:6007/udp
Allow from a Specific IP Address
# Allow all traffic from a trusted IP
sudo ufw allow from 203.0.113.50
# Allow SSH from a specific IP only
sudo ufw allow from 203.0.113.50 to any port 22
# Allow access to MySQL from a specific application server
sudo ufw allow from 10.0.1.20 to any port 3306
Allow a Subnet
# Allow all traffic from a local network
sudo ufw allow from 10.0.1.0/24
# Allow SSH from your office network only
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
Deny Specific Traffic
# Block a specific IP address
sudo ufw deny from 203.0.113.100
# Block access to a specific port from an IP
sudo ufw deny from 203.0.113.100 to any port 80
Delete Rules
There are two ways to delete rules:
By rule number:
# List rules with numbers
sudo ufw status numbered
# Delete rule number 3
sudo ufw delete 3
By rule specification:
sudo ufw delete allow 80/tcp
sudo ufw delete allow from 203.0.113.50
Application Profiles
UFW supports application profiles that bundle firewall rules for specific software. Profiles are stored as INI files in /etc/ufw/applications.d/.
Listing Available Profiles
sudo ufw app list
Common output:
Available applications:
Nginx Full
Nginx HTTP
Nginx HTTPS
OpenSSH
Using Application Profiles
# Allow Nginx HTTP and HTTPS
sudo ufw allow 'Nginx Full'
# Allow only HTTPS
sudo ufw allow 'Nginx HTTPS'
# Allow OpenSSH
sudo ufw allow OpenSSH
Viewing Profile Details
sudo ufw app info 'Nginx Full'
Output:
Profile: Nginx Full
Title: Web Server (Nginx, HTTP + HTTPS)
Description: Small, but very powerful and efficient web server
Ports:
80,443/tcp
Creating Custom Application Profiles
Create a file in /etc/ufw/applications.d/ for your application. For example, for a Node.js application:
sudo nano /etc/ufw/applications.d/myapp
Contents:
[MyApp]
title=My Node.js Application
description=Express.js web application
ports=3000/tcp
[MyApp Full]
title=My Node.js Application (Full)
description=Express.js web application with WebSocket
ports=3000/tcp|8080/tcp
Then enable it:
sudo ufw allow MyApp
Rate Limiting
UFW includes a built-in rate limiting feature that is particularly useful for protecting against brute-force attacks on SSH and other login services:
sudo ufw limit ssh
This limits incoming SSH connections to 6 connections within 30 seconds from a single IP address. Connections beyond this threshold are denied. The rule translates to the following iptables configuration:
- Allow connections if there have been fewer than 6 connection attempts in the last 30 seconds
- Deny connections exceeding this rate
Rate limiting is recommended for:
- SSH (port 22)
- FTP (port 21)
- Any login or authentication endpoint
# Rate limit SSH with explicit port
sudo ufw limit 22/tcp
# Rate limit a custom service
sudo ufw limit 2222/tcp
Note: Rate limiting in UFW is basic compared to tools like fail2ban. For production servers, consider using both UFW for firewall rules and fail2ban for advanced brute-force protection.
Configuring Logging
UFW logging helps you monitor blocked connections and detect potential attacks. Logs are written to /var/log/ufw.log.
Setting the Log Level
# Available levels: off, low, medium, high, full
sudo ufw logging on # Default: low
sudo ufw logging medium # More detail
sudo ufw logging high # Verbose
| Level | What It Logs |
|---|---|
| off | No logging |
| low | Blocked packets matching default policy |
| medium | Low + packets not matching rules, invalid packets |
| high | Medium + rate-limited packets |
| full | Everything without rate limiting |
Reading UFW Logs
# View recent UFW log entries
sudo tail -50 /var/log/ufw.log
# Follow the log in real time
sudo tail -f /var/log/ufw.log
# Filter for blocked connections
sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | tail -20
A typical log entry looks like this:
Feb 12 14:23:45 server kernel: [UFW BLOCK] IN=eth0 OUT= MAC=... SRC=203.0.113.100 DST=10.0.1.5 LEN=44 TOS=0x00 PREC=0x00 TTL=50 ID=12345 PROTO=TCP SPT=54321 DPT=22 WINDOW=65535 RES=0x00 SYN URGP=0
Key fields:
- SRC: Source IP address of the blocked connection
- DST: Destination (your server) IP address
- PROTO: Protocol (TCP/UDP)
- SPT: Source port
- DPT: Destination port (the port the attacker tried to reach)
IPv6 Support
UFW handles IPv6 by default. Verify it is enabled:
sudo nano /etc/default/ufw
Ensure this line is set to yes:
IPV6=yes
When you add a rule, UFW automatically creates both IPv4 and IPv6 rules:
sudo ufw allow 80/tcp
# Creates rules for both IPv4 and IPv6
To create an IPv6-only rule:
sudo ufw allow from 2001:db8::/32 to any port 22 proto tcp
Practical Server Configurations
Web Server (Nginx or Apache)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Database Server (MySQL/PostgreSQL)
Only allow database connections from your application servers:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
# Allow MySQL from app server only
sudo ufw allow from 10.0.1.20 to any port 3306
# Allow PostgreSQL from app server only
sudo ufw allow from 10.0.1.20 to any port 5432
sudo ufw enable
Docker Host
Docker modifies iptables directly, which can bypass UFW rules. To prevent this, add the following to /etc/docker/daemon.json:
{
"iptables": false
}
Then restart Docker:
sudo systemctl restart docker
Warning: Disabling Docker’s iptables management means you must manually configure all port forwarding for container-published ports through UFW. This is a trade-off between security control and convenience.
Alternatively, use the DOCKER-USER chain in iptables for more granular control without completely disabling Docker networking.
Mail Server
sudo ufw allow ssh
sudo ufw allow 25/tcp # SMTP
sudo ufw allow 587/tcp # SMTP Submission
sudo ufw allow 993/tcp # IMAPS
sudo ufw allow 995/tcp # POP3S
sudo ufw allow 80/tcp # HTTP (for webmail)
sudo ufw allow 443/tcp # HTTPS (for webmail)
VPN Server (WireGuard)
sudo ufw allow ssh
sudo ufw allow 51820/udp # WireGuard
Also enable IP forwarding in UFW. Edit /etc/ufw/sysctl.conf:
net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
And add routing rules in /etc/ufw/before.rules (before the *filter section):
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
COMMIT
Advanced UFW Configuration
Editing Raw iptables Rules
For advanced use cases not covered by UFW commands, you can edit the raw rules files:
/etc/ufw/before.rules— Rules applied before UFW rules/etc/ufw/after.rules— Rules applied after UFW rules/etc/ufw/user.rules— UFW-generated rules (do not edit directly)
After editing these files, reload UFW:
sudo ufw reload
Allowing Specific Network Interfaces
# Allow HTTP only on the public interface
sudo ufw allow in on eth0 to any port 80
# Allow database connections only on the private interface
sudo ufw allow in on eth1 to any port 3306
Inserting Rules at a Specific Position
Rule order matters in UFW. Rules are evaluated from top to bottom, and the first matching rule wins:
# Insert a deny rule at position 1 (before existing allow rules)
sudo ufw insert 1 deny from 203.0.113.100 to any port 80
Troubleshooting
Cannot Connect After Enabling UFW
If you lose access to a remote server:
- Access the server through a console (cloud provider’s web console, KVM, or physical access)
- Disable UFW:
sudo ufw disable - Review your rules:
sudo ufw status numbered - Fix the configuration and re-enable
Rules Not Working as Expected
# Show detailed rule information including rule numbers
sudo ufw status numbered
# Show full verbose output
sudo ufw status verbose
# Check the underlying iptables rules
sudo iptables -L -n -v
UFW Fails to Start
Check the UFW configuration for syntax errors:
# Verify the configuration
sudo ufw show raw
# Check system logs
sudo journalctl -u ufw
Reset Everything
If the configuration becomes too complex or broken:
sudo ufw reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw enable
UFW Management Commands Reference
| Command | Description |
|---|---|
sudo ufw enable | Enable the firewall |
sudo ufw disable | Disable the firewall |
sudo ufw status | Show current rules |
sudo ufw status verbose | Show rules with details |
sudo ufw status numbered | Show rules with numbers |
sudo ufw reload | Reload rules without restart |
sudo ufw reset | Reset to defaults (disables UFW) |
sudo ufw allow <port> | Allow incoming traffic on port |
sudo ufw deny <port> | Deny incoming traffic on port |
sudo ufw limit <port> | Rate-limit connections on port |
sudo ufw delete <rule> | Delete a specific rule |
sudo ufw logging <level> | Set logging level |
sudo ufw app list | List application profiles |
sudo ufw show added | Show rules added in current session |
Summary
UFW provides a straightforward way to manage firewall rules on Ubuntu servers without the complexity of raw iptables commands. By following the principle of least privilege — deny all incoming traffic by default and only allow what is explicitly needed — you establish a strong security foundation for your server.
Key takeaways:
- Always allow SSH before enabling UFW on remote servers
- Use application profiles for common services like Nginx and OpenSSH
- Enable rate limiting on authentication ports to mitigate brute-force attacks
- Configure logging to monitor for suspicious activity
- Be aware of Docker’s iptables modifications and configure accordingly
- Regularly review your rules with
sudo ufw status numberedand remove unnecessary entries
For additional server hardening, combine UFW with other security measures. See our guides on SSH Hardening and WireGuard VPN Setup.