UFW Firewall Guide: Secure Your Linux Server in Minutes

UFW Firewall Guide: Secure Your Linux Server in Minutes


If you’re running a Linux server — whether it’s a VPS, a Raspberry Pi, or a cloud instance — a firewall is one of the first things you should set up. UFW (Uncomplicated Firewall) is the default firewall management tool on Ubuntu and Debian-based systems. It provides a simple, human-readable interface on top of the powerful iptables framework, letting you lock down your server in minutes instead of hours.

This guide covers everything from installation to advanced rules, giving you a production-ready firewall configuration.

Why You Need a Firewall

Every server connected to the internet is constantly being probed. Automated bots scan IP ranges looking for open ports — SSH on port 22, databases on 3306 or 5432, Redis on 6379, and more. Without a firewall, every listening service on your server is exposed to the world.

A firewall lets you:

  • Block all incoming traffic by default and only allow what you explicitly need
  • Rate-limit brute force attacks on SSH and other services
  • Restrict access to internal services like databases to specific IPs
  • Log suspicious connection attempts for monitoring and auditing

UFW makes all of this straightforward with simple commands.

Installing UFW

UFW comes pre-installed on Ubuntu. On Debian and other distributions, install it with:

sudo apt update
sudo apt install ufw

Verify the installation:

sudo ufw version

You should see output like ufw 0.36.2 or similar. At this point, UFW is installed but not active — no rules are being enforced yet.

Understanding Default Policies

Before enabling UFW, you need to understand default policies. These determine what happens to traffic that doesn’t match any specific rule.

The recommended setup for a server:

sudo ufw default deny incoming
sudo ufw default allow outgoing

This means:

  • Incoming traffic: Blocked unless you create a rule to allow it
  • Outgoing traffic: Allowed (your server can reach the internet for updates, API calls, etc.)

This is a “deny by default” approach — the most secure starting point.

CRITICAL: Allow SSH Before Enabling

This is the most important step. If you enable UFW without allowing SSH first, you’ll lock yourself out of your server immediately. If you’re connected via SSH (which you almost certainly are on a remote server), run this first:

sudo ufw allow ssh

This is equivalent to:

sudo ufw allow 22/tcp

If you’ve changed your SSH port to something else (e.g., 2222), allow that instead:

sudo ufw allow 2222/tcp

Enabling UFW

Once your SSH rule is in place, enable the firewall:

sudo ufw enable

You’ll see a warning: Command may disrupt existing ssh connections. Proceed with operation (y|n)? — type y and press Enter. Your existing SSH session will remain active.

Check the status:

sudo ufw status verbose

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)

Your firewall is now active and protecting your server.

Common Rules for Web Servers

Allow HTTP and HTTPS

If you’re running a web server (Nginx, Apache, Caddy), you need ports 80 and 443:

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

Or allow both at once using a port range:

sudo ufw allow 80,443/tcp

Allow a Specific Port

For any custom service, specify the port and protocol:

sudo ufw allow 8080/tcp
sudo ufw allow 51820/udp   # WireGuard VPN

Allow a Port Range

Some applications use a range of ports:

sudo ufw allow 6000:6007/tcp

Allow Traffic from a Specific IP

Restrict a port to only accept connections from a trusted IP address. This is essential for databases and admin panels:

# Only allow your office IP to connect to MySQL
sudo ufw allow from 203.0.113.50 to any port 3306

# Only allow your home IP to SSH in
sudo ufw allow from 198.51.100.25 to any port 22

Allow an Entire Subnet

Useful for local networks or VPN subnets:

sudo ufw allow from 10.8.0.0/24
sudo ufw allow from 192.168.1.0/24 to any port 22

Denying Traffic

While the default policy already denies incoming traffic, you might want explicit deny rules — for example, to block a specific IP:

# Block a specific IP entirely
sudo ufw deny from 23.94.168.100

# Block a specific IP from a specific port
sudo ufw deny from 23.94.168.100 to any port 22

To block an entire subnet:

sudo ufw deny from 103.25.0.0/16

Rate Limiting SSH

One of UFW’s best features is built-in rate limiting. This automatically blocks IP addresses that attempt too many connections in a short period — ideal for stopping SSH brute force attacks:

sudo ufw limit ssh

This replaces your existing allow ssh rule. It allows a maximum of 6 connections within 30 seconds from a single IP. After that, further connections are blocked temporarily.

Check it’s applied:

sudo ufw status
To                         Action      From
--                         ------      ----
22/tcp                     LIMIT       Anywhere
22/tcp (v6)                LIMIT       Anywhere (v6)

This is far simpler than setting up fail2ban for basic protection (though fail2ban is still excellent for more advanced scenarios).

Using Application Profiles

UFW supports application profiles — predefined rule sets for common software. List available profiles:

sudo ufw app list

Common output:

Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH

Use a profile instead of remembering port numbers:

sudo ufw allow 'Nginx Full'
sudo ufw allow 'OpenSSH'

View what a profile includes:

sudo ufw app info 'Nginx Full'
Profile: Nginx Full
Title: Web Server (Nginx, HTTP + HTTPS)
Description: Small, but very powerful and efficient web server

Ports:
  80,443/tcp

Applications install their own profiles in /etc/ufw/applications.d/. You can create custom profiles for your own services too.

Deleting Rules

Delete by Rule

Remove a specific rule by repeating the command with delete:

sudo ufw delete allow 8080/tcp
sudo ufw delete deny from 23.94.168.100

Delete by Number

List rules with numbers:

sudo ufw status numbered
Status: active

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

Delete by number:

sudo ufw delete 2

Note: After deleting a rule, the numbers shift. Always re-check ufw status numbered before deleting another rule by number.

Checking Firewall Logs

UFW can log blocked and allowed connections. Set the logging level:

sudo ufw logging on        # Default: low
sudo ufw logging medium    # More detail
sudo ufw logging high      # Full verbosity

Logs are written to /var/log/ufw.log. View recent blocked connections:

sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | tail -20

A typical blocked entry looks like:

Feb 15 14:23:01 server kernel: [UFW BLOCK] IN=eth0 OUT= MAC=... SRC=185.220.101.34 DST=10.0.0.5 ... DPT=3389 ...

This shows someone tried to connect to port 3389 (RDP) from IP 185.220.101.34 and was blocked.

Resetting UFW

If your rules get messy and you want to start over:

sudo ufw reset

This disables UFW and deletes all rules. You’ll need to set default policies and re-add your rules from scratch. Be especially careful on remote servers — make sure you have console access (not just SSH) before resetting.

A Complete Server Setup Example

Here’s a real-world example for a typical web server running Nginx with a PostgreSQL database, SSH on a custom port, and WireGuard VPN:

# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH on custom port with rate limiting
sudo ufw limit 2222/tcp comment 'SSH rate limited'

# Allow web traffic
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'

# Allow WireGuard VPN
sudo ufw allow 51820/udp comment 'WireGuard'

# Allow PostgreSQL only from VPN subnet
sudo ufw allow from 10.8.0.0/24 to any port 5432 comment 'PostgreSQL via VPN'

# Allow Redis only from localhost
sudo ufw allow from 127.0.0.1 to any port 6379 comment 'Redis local only'

# Enable the firewall
sudo ufw enable

# Verify
sudo ufw status verbose

This gives you:

  • SSH protected with rate limiting on a non-standard port
  • Web ports open for public traffic
  • WireGuard accessible for VPN clients
  • PostgreSQL only reachable through the VPN tunnel
  • Redis only accessible from the server itself
  • Everything else blocked

UFW with Docker — An Important Caveat

If you’re running Docker, be aware that Docker modifies iptables directly and bypasses UFW entirely. This means a container exposing port 3306 will be accessible from the internet even if UFW blocks that port.

To fix this, edit /etc/docker/daemon.json:

{
  "iptables": false
}

Then restart Docker:

sudo systemctl restart docker

With "iptables": false, Docker won’t create its own iptables rules, and UFW will properly control access. However, you’ll need to manually set up routing for container networking. For most setups, using Docker’s --network host mode or binding to 127.0.0.1 is a simpler solution:

# Bind container ports to localhost only
docker run -p 127.0.0.1:3306:3306 mysql:8

This ensures the MySQL container is only accessible from the server itself, and you can use Nginx or a reverse proxy to expose what you need.

Disabling and Re-enabling

Temporarily disable UFW without removing rules:

sudo ufw disable

Re-enable with all rules intact:

sudo ufw enable

Quick Reference

CommandDescription
sudo ufw enableActivate the firewall
sudo ufw disableDeactivate (rules preserved)
sudo ufw status verboseShow all rules and policies
sudo ufw status numberedShow rules with numbers
sudo ufw allow 80/tcpAllow port 80 TCP
sudo ufw deny from IPBlock a specific IP
sudo ufw limit sshRate limit SSH
sudo ufw delete allow 80/tcpRemove a rule
sudo ufw resetRemove all rules and disable
sudo ufw logging onEnable logging
sudo ufw app listList application profiles

Wrapping Up

UFW turns Linux firewall management from a complex iptables exercise into something you can configure in under five minutes. The key principles are simple:

  1. Deny everything by default
  2. Allow only what you need
  3. Rate limit SSH
  4. Restrict databases to trusted IPs or localhost
  5. Check your rules after setup

Combined with SSH key authentication (covered in our SSH key guide) and regular system updates, UFW gives your server a solid security foundation. If you’re running Docker, remember the iptables caveat and bind container ports to localhost when possible.

Your server doesn’t need to be an open door. A few ufw commands is all it takes to close it.