What is UFW?

UFW is a command-line interface to iptables, which is the Linux kernel's packet filtering engine. You do not need to understand iptables to use UFW. That is the point.

It ships with Ubuntu since version 8.04 and is available on all Debian-based distributions: Debian, Raspberry Pi OS, Ubuntu Server. The package is called ufw. Check if it is installed:

sudo ufw version
# Should return something like: ufw 0.36.1

If not installed:

sudo apt update && sudo apt install -y ufw

UFW is not enabled by default. Run sudo ufw status and you will most likely see Status: inactive. That is what we are fixing.

⚠️ Allow SSH BEFORE you enable. Run sudo ufw allow ssh before you run sudo ufw enable. Skip this on a remote server and you lock yourself out with no way back in without physical access to the machine.

Default policy: deny all incoming

Start with two rules that cover all traffic you do not explicitly allow:

sudo ufw default deny incoming   # block all inbound by default
sudo ufw default allow outgoing  # allow all outbound by default

These two lines are the foundation. Everything you add after is an exception to the inbound rule.

Allow SSH before anything else

Do this now, before enabling the firewall:

# Short form (uses /etc/services to look up port 22)
sudo ufw allow ssh

# Or explicit:
sudo ufw allow 22/tcp
💡 SSH from LAN only: If you only access SSH from your local network, you can restrict it to your LAN subnet instead of leaving it open to any address. See the LAN restriction section below.

Allow the services you actually run

Open only the ports you use. Examples:

# HTTP and HTTPS (web server)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# DNS (Pi-hole or AdGuard Home)
sudo ufw allow 53

# WireGuard VPN
sudo ufw allow 51820/udp

# AdGuard Home initial setup port (only needed during setup)
sudo ufw allow 3000/tcp

Named profiles

UFW supports named profiles for known applications. Profiles live in /etc/ufw/applications.d/. List what is available:

sudo ufw app list

Allow a profile:

sudo ufw allow "OpenSSH"       # SSH via named profile
sudo ufw allow "Nginx Full"    # HTTP + HTTPS via named profile
📝 Cloudflare Tunnel (cloudflared): If you run cloudflared, you do not need to open any inbound port. cloudflared makes an outbound connection to Cloudflare's network. Outbound traffic is already allowed by the default policy.

LAN restriction: allow only from your local network

Instead of opening a port to the whole internet, you can restrict access to your LAN subnet. This is good practice for SSH, DNS and admin interfaces:

# SSH from 192.168.1.x only
sudo ufw allow from 192.168.1.0/24 to any port 22

# DNS from LAN only
sudo ufw allow from 192.168.1.0/24 to any port 53

# Pi-hole admin (port 80) from LAN only
sudo ufw allow from 192.168.1.0/24 to any port 80

Adjust 192.168.1.0/24 to your actual subnet. Check with ip route and look for the line starting with your LAN interface (typically eth0 or wlan0).

Enable the firewall

Once your rules are in place:

sudo ufw enable

UFW asks for confirmation. Type y. The firewall is now active and persists across reboots automatically.

Check status and rules

sudo ufw status verbose

Output shows active rules, default policies and IPv6 status. verbose gives more detail than plain status.

From another machine on LAN you can verify with nmap:

nmap -p 22,53,80,443 [your-pi-ip]

Ports you opened should show as open. Ports you did not open should show as filtered.

Rate limiting SSH

UFW can rate-limit SSH connections: addresses that exceed 6 attempts in 30 seconds get temporarily blocked. One command:

sudo ufw limit ssh

It is not a full replacement for fail2ban, but it is considerably better than nothing and requires zero configuration.

Delete rules

Delete a rule using the exact syntax you used to add it:

sudo ufw delete allow 80/tcp

Alternatively, delete by rule number:

sudo ufw status numbered      # show rules with numbers
sudo ufw delete 3             # delete rule number 3

Logging

UFW can log blocked traffic to /var/log/ufw.log:

sudo ufw logging on

Use low, medium or high to control the volume of log output. on is equivalent to low. Watch the log live:

sudo tail -f /var/log/ufw.log

IPv6

UFW supports IPv6. Check that IPV6=yes is set in /etc/default/ufw. It is the default on modern systems, but worth verifying:

grep IPV6 /etc/default/ufw
# Should return: IPV6=yes

When IPv6 is enabled, UFW automatically creates corresponding IPv6 rules for any port you open.

Complete Pi homelab example

A Raspberry Pi running Pi-hole and SSH, accessible only from LAN. From scratch:

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

# SSH from LAN only (do this BEFORE enable)
sudo ufw allow from 192.168.1.0/24 to any port 22

# DNS from LAN (Pi-hole/AdGuard)
sudo ufw allow from 192.168.1.0/24 to any port 53

# Pi-hole admin interface from LAN
sudo ufw allow from 192.168.1.0/24 to any port 80

# Rate limit SSH
sudo ufw limit ssh

# Enable logging
sudo ufw logging on

# Enable
sudo ufw enable

# Verify
sudo ufw status verbose
📝 Running Pi-hole or AdGuard? Remember to open port 53 (DNS) and port 80 (admin) from your LAN, as shown above. AdGuard also uses port 3000 during initial setup; remove it afterwards with sudo ufw delete allow 3000/tcp.

What UFW does not do

UFW is a network firewall tool. It is not an intrusion detection system. It does not inspect packet contents. It has no idea whether the traffic passing through port 22 is actually SSH or something else that happens to use that port.

It does not protect against attacks from machines on your own LAN. If you have LAN rules open for 192.168.1.0/24, any device on that network can reach those ports.

It is not a replacement for a VPN for remote access. If you need secure access to your network from outside, WireGuard is the right tool.

Sources