How to set up the UFW firewall on Ubuntu in 5 steps

Ubuntu ships with UFW installed, but turned off. People who spin up a VPS, install nginx, Postgres and Redis and walk away often end up with half a dozen ports open to the internet without realizing it. UFW (Uncomplicated Firewall) is a simple layer on top of iptables/nftables that fixes this with a handful of commands. This tutorial walks through it step by step, including the classic mistake that locks you out of your own server.
What UFW is and why use it
What is UFW on Ubuntu?
UFW is Ubuntu's command-line front end for managing the kernel firewall (netfilter) without writing iptables rules by hand. You define which inbound and outbound connections are allowed or blocked, and it translates that into the real rules.
The security logic is least privilege: deny everything inbound by default and allow only what the server actually needs to offer. A forgotten service listening on 0.0.0.0 stops being a problem, because the firewall blocks it before any packet reaches it.
Step 1: check the current state
sudo ufw status verboseOn a fresh install, the answer is Status: inactive. Before enabling anything, see what is actually listening:
sudo ss -tulpnThis command lists open TCP/UDP ports and the process that owns each one. It is your list of what needs to be allowed — or closed. If you use Windows, the PowerShell equivalent is in the post on Get-NetTCPConnection to see open ports.
Step 2: set the default policy
sudo ufw default deny incoming
sudo ufw default allow outgoingThe first line blocks every inbound connection without an explicit rule. The second lets the server talk to the world (updates, DNS, APIs). This is UFW's own default, but stating it explicitly avoids surprises if someone edited /etc/default/ufw.
Step 3: allow SSH BEFORE enabling
This is the mistake that bites VPS users: enabling the firewall with a deny incoming policy without having allowed the SSH port. Your current session usually stays alive, but every new connection is refused — and if the session drops, you've lost access (only the provider's console can save you).
sudo ufw allow OpenSSHThe OpenSSH profile opens port 22/tcp. If you changed the port in sshd_config, allow the right number:
sudo ufw allow 2222/tcpEven better: instead of opening it to everyone, use limit, which temporarily blocks IPs that make 6 or more connection attempts within 30 seconds:
sudo ufw limit OpenSSHIt's a basic defense against brute force. For something more sophisticated, combine it with fail2ban, which reads the logs and bans for longer. And the firewall doesn't replace configuring the daemon itself: see the 7 essential SSH hardening settings.
Step 4: allow only the services you need
Common examples:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 5432 proto tcp- Lines 1 and 2: web (HTTP and HTTPS) open to any source.
- Line 3: SSH only from a specific IP (replace
203.0.113.10with your fixed IP or your VPN's). It's the strongest rule against scanners. - Line 4: database reachable only from the local network. A database exposed to the internet is one of the most common causes of data leaks.
If you use the single-IP rule for SSH, remove the generic SSH rule after testing a new connection in another terminal.
Step 5: enable and verify
sudo ufw enable
sudo ufw status numberedUFW warns that enabling may disrupt existing SSH connections; confirm with y only if step 3 is done. The numbered listing shows each rule with an index. To delete a rule:
sudo ufw delete 3The numbers shift after each removal, so run status numbered again before deleting the next one. To turn on logging of blocked attempts:
sudo ufw logging onBlocks show up in /var/log/ufw.log (or in the journal, depending on the setup). It's worth a look now and then: it's a snapshot of the kind of scanning your IP receives.
Gotchas almost nobody mentions
Does Docker bypass UFW rules?
Yes: ports published with -p 8080:80 in Docker are opened directly in iptables rules and bypass UFW. Docker's own documentation warns that UFW can't block that traffic.
In practice, a database container with -p 5432:5432 is reachable from the internet even with deny incoming active. Two ways out: publish the port on loopback only (-p 127.0.0.1:5432:5432) or, when the service only needs to talk to other containers, publish no port at all.
What about IPv6?
Check that IPV6=yes is set in /etc/default/ufw. With it on, the rules apply to both IPv4 and IPv6. Without it, the server may be protected on v4 and wide open on v6.
How do I test from the outside?
From your own computer (not the server), run nmap -Pn SERVER-IP and compare against the list of ports you decided to allow. Only scan servers you own or have authorization to test.
A firewall is one layer, not the solution
UFW shrinks the attack surface, but it doesn't fix weak passwords, outdated software or leaked credentials. Treat it as the first locked door in a house with several. If you manage remote access on Windows, the reasoning is the same, and the post on how to protect RDP against brute force shows how to apply it. Start today with ss -tulpn on your server: whatever you don't recognize in that list is the first candidate to close.




Comments