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
LevelWhat It Logs
offNo logging
lowBlocked packets matching default policy
mediumLow + packets not matching rules, invalid packets
highMedium + rate-limited packets
fullEverything 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:

  1. Access the server through a console (cloud provider’s web console, KVM, or physical access)
  2. Disable UFW: sudo ufw disable
  3. Review your rules: sudo ufw status numbered
  4. 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

CommandDescription
sudo ufw enableEnable the firewall
sudo ufw disableDisable the firewall
sudo ufw statusShow current rules
sudo ufw status verboseShow rules with details
sudo ufw status numberedShow rules with numbers
sudo ufw reloadReload rules without restart
sudo ufw resetReset 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 listList application profiles
sudo ufw show addedShow 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 numbered and remove unnecessary entries

For additional server hardening, combine UFW with other security measures. See our guides on SSH Hardening and WireGuard VPN Setup.