How to Configure UFW Firewall on Linux

UFW makes iptables manageable — but most tutorials skip the IPv6 caveat that leaves half your firewall off. Here's the full setup.

4 min read
How to Configure UFW Firewall on Linux

UFW — Uncomplicated Firewall — ships with Ubuntu but starts disabled. Out of the box, your server accepts connections on every port. UFW is the tool that changes that, and it's a frontend for iptables/nftables, which means it's producing real kernel-level packet filtering rules, not some lightweight wrapper.

I touched on this briefly in the first 24 hours on a new VPS guide. This is the dedicated deep dive.

Before You Touch Anything

This is the mistake that fills forum threads with panicked messages at 2am. If you enable UFW before adding an SSH rule, you will lock yourself out immediately. The default policy blocks all incoming traffic — including the SSH session you're currently sitting in.

Correct order: set your rules first, then enable UFW. Not the other way around.

If you do get locked out, your VPS provider's web console is the recovery path. Every major provider — DigitalOcean, Hetzner, Vultr, Linode/Akamai — gives you out-of-band terminal access through their control panel. Use it to add the SSH rule and run ufw enable.

The IPv6 Caveat

Open /etc/default/ufw and check this line:

IPV6=yes

If it says no or doesn't exist, your UFW rules only apply to IPv4. The ip6tables rules — the ones that would filter IPv6 traffic — simply don't get created.

This matters because Ubuntu 20.04+ assigns both an IPv4 and IPv6 address by default on most cloud providers. If your IPv4 firewall is locked down but IPv6 is wide open, an attacker reaching your server via IPv6 bypasses every rule you've set. Most tutorials either don't mention this or mention it in passing. It's not a footnote — check it first.

sudo nano /etc/default/ufw

Set IPV6=yes, save, and continue.

Setting Default Policies

The starting posture: deny all inbound, allow all outbound.

sudo ufw default deny incoming
sudo ufw default allow outgoing

This means your server can reach out to the internet freely (software updates, API calls, whatever), but nothing can reach in unless you explicitly allow it. That's the correct default for almost every server.

Adding Rules

SSH

Do this before anything else — before enabling UFW:

sudo ufw allow ssh

This opens port 22/tcp using the service name. If your SSH daemon is on a non-standard port, use the explicit port instead:

sudo ufw allow 2222/tcp

ufw allow ssh when SSH is on port 2222 does nothing useful — it opens 22, not 2222.

For servers where you want to lock SSH down to a specific trusted IP:

sudo ufw allow from 203.0.113.50 to any port 22

Replace 203.0.113.50 with your actual IP. This is covered in the Linux system hardening guide and it's worth doing on any server where you have a stable source IP.

Web Traffic

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

Or, if Nginx is installed and you want to use UFW's application profiles:

sudo ufw allow 'Nginx Full'

ufw app list shows all available profiles. Application profiles are defined in /etc/ufw/applications.d/ and most major packages install their own.

Everything Else

Add rules for any other services you're running — database ports (only from specific IPs, never open to the world), monitoring agents, whatever applies. The pattern is consistent: ufw allow from [source] to any port [port] for restricted access, ufw allow [port]/tcp for public access.

Rate Limiting on SSH

UFW has built-in rate limiting:

sudo ufw limit ssh

This blocks IPs that make six or more new SSH connections within 30 seconds. It's less configurable than fail2ban, but it's one command with zero setup. Worth adding even if you're also running fail2ban or CrowdSec — defense in depth doesn't require picking one.

Enable It

Once your rules are set:

sudo ufw enable

You'll get a warning that this may disrupt existing SSH connections. If you've already added an SSH rule, confirm and continue.

Logging

sudo ufw logging medium

This logs blocked packets and new allowed connections to /var/log/ufw.log. The low setting logs only blocked packets. high and full get noisy fast on a public IP — every probe, every scan, every stray packet. Medium is the practical balance.

If you're running logwatch or shipping logs to a SIEM, UFW log entries are parseable and well-formatted.

Managing Rules

Check current rules:

sudo ufw status
sudo ufw status verbose

verbose adds the default policies to the output, which is helpful to confirm your deny/allow defaults are set correctly.

Numbered view for easy deletion:

sudo ufw status numbered

This outputs something like:

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

To delete a rule by number:

sudo ufw delete 2

Deleting by specification works too:

sudo ufw delete allow 80/tcp

But numbered deletion is easier, especially when you have IP-restricted rules where typing out the full specification is error-prone.

After changes:

sudo ufw reload

A Note on Rule Order

UFW evaluates rules in order — first match wins. For most setups this doesn't create problems because you're either allowing or denying specific ports. It matters when you're mixing IP-restricted and open rules for the same port. If you add ufw allow 22 before ufw allow from 203.0.113.50 to any port 22, the first rule matches all traffic to port 22 before the specific-IP rule is ever checked. The specific-IP rule becomes meaningless.

If you're building a tighter SSH posture (IP-restricted), the pattern is:

  1. Allow from trusted IP to port 22
  2. Deny all other traffic to port 22
sudo ufw allow from 203.0.113.50 to any port 22
sudo ufw deny 22

The deny rule is a belt-and-suspenders addition when the default incoming policy is already deny — it's redundant in most cases, but explicit rules are easier to audit than implied defaults.

Quick Reference

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

# Add rules
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw limit ssh

# Enable
sudo ufw enable

# Check
sudo ufw status verbose
sudo ufw status numbered

# Manage
sudo ufw delete [number]
sudo ufw reload

# Logging
sudo ufw logging medium

UFW won't stop every attack — nothing does that alone. What it does is close the surface area. Everything except what you've explicitly opened is unreachable by default. Combined with key-based SSH auth, fail2ban or CrowdSec for brute-force defense, and unattended security updates, you've got a server that's genuinely difficult to compromise through its network-facing surface. That's the goal.

## Convertkit Newsletter