UFW Firewall Rules for a Public VPS: 2026 Setup

A firewall decides which network ports on your VPS the outside world can reach. Without one, every service you start — a database, a stray dev server, a debugging tool — is exposed to the entire internet the instant it binds to a public interface. Ubuntu ships with UFW (Uncomplicated Firewall), a friendly front end to the kernel's nftables backend, and configuring it well takes about five minutes. This guide sets up a sensible default-deny firewall for a public server and shows how to verify it before it ever has a chance to lock you out.

Quick Verdict
The correct posture for a public VPS is default-deny inbound, allow-all outbound: block everything coming in, then open only the ports you actually serve. Always add the SSH allow rule before enabling UFW, or you will cut your own connection. Use ufw limit rather than ufw allow for SSH to throttle brute-force attempts at the firewall layer.

Check What Is Already Listening

Before you write a single rule, find out which ports are actually open. This tells you what you need to allow and often reveals services you forgot were running:

1sudo ss -tulpn

The output lists every listening socket, the protocol, the port, and the process. Anything bound to 0.0.0.0 or [::] is reachable from the public internet. Make a note of what you genuinely need exposed — typically SSH, and on a web server, HTTP and HTTPS. Everything else should be firewalled off, even if it is running.


Set the Default Policy

UFW's power comes from setting a baseline first, then poking specific holes. Establish default-deny for incoming traffic and default-allow for outgoing:

1sudo ufw default deny incoming
2sudo ufw default allow outgoing

These commands do not take effect until you enable UFW, so they are safe to run now. deny incoming means an inbound packet that matches no explicit allow rule is dropped. allow outgoing lets your server make connections out — fetch package updates, call APIs, send mail — without you whitelisting every destination.


Allow SSH Before Anything Else

This is the rule that keeps you from being locked out. Critically, allow SSH before you enable the firewall:

1sudo ufw limit 22/tcp

Note limit instead of allow. The limit rule permits SSH but blocks any source IP that opens more than six connections in thirty seconds — exactly the signature of a brute-force script. Legitimate logins are unaffected; password-guessing bots get throttled at the network edge before they ever reach sshd.

If you moved SSH to a custom port during host hardening, substitute it here:

1sudo ufw limit 2222/tcp

Open Your Web Ports

For a server running a website or API, allow the standard web ports. UFW knows the common service names, so either form works:

1sudo ufw allow 80/tcp     # HTTP
2sudo ufw allow 443/tcp    # HTTPS

Or use the named profiles that Nginx and Apache register on install:

1sudo ufw allow 'Nginx Full'

Nginx Full opens both 80 and 443 in one rule. Run sudo ufw app list to see which application profiles are available on your system.

Only open ports for services that genuinely need public access. A database such as PostgreSQL or MySQL should almost never be reachable from the internet — keep its port closed and connect over SSH tunnels or a private network instead.


Enable the Firewall

With the SSH rule in place, enable UFW. It warns that the operation may disrupt existing connections — because you allowed SSH first, your session survives:

1sudo ufw enable

Confirm with y. UFW is now active and will persist across reboots automatically.


Verify Your Rules

Never assume the firewall does what you intended — check it. The verbose status output is the authoritative view:

1sudo ufw status verbose

You should see Default: deny (incoming), allow (outgoing) at the top, followed by your allow and limit rules. Each line shows the port, the action, and the source. Read it carefully: a missing HTTPS rule means your site is unreachable on 443, and an accidental allow where you meant limit weakens SSH protection.

Test from outside the server, too. From your local machine, confirm an open port responds and a closed one does not:

1nc -zv your-server-ip 443    # should connect
2nc -zv your-server-ip 5432   # should be refused or time out

Manage and Remove Rules

Rules are numbered, which makes editing precise. List them with numbers:

1sudo ufw status numbered

To remove a rule, reference its number — useful when you close a port you no longer serve:

1sudo ufw delete 3

If you ever need to start over, sudo ufw reset wipes all rules and disables the firewall. After a reset, you must re-add the SSH allow rule before re-enabling, exactly as the first time.


Turn On Logging

A firewall you cannot see is hard to trust. UFW can log the packets it blocks, which is invaluable when a service is unexpectedly unreachable or you want to see what is probing you:

1sudo ufw logging on

By default this logs at the low level — blocked packets and a sample of allowed ones. Entries land in /var/log/ufw.log and the system journal. Tail them to watch the firewall act:

1sudo grep 'UFW BLOCK' /var/log/ufw.log | tail

If you are debugging a connection that should work but does not, this log answers the first question every time: is the firewall the thing dropping it? A matching UFW BLOCK line for your port means you are missing an allow rule; no line means the problem is elsewhere, such as the service not listening. Set logging back to off only if the volume becomes noisy on a heavily scanned host.

Note that UFW manages IPv6 and IPv4 rules together when IPV6=yes is set in /etc/default/ufw — the Ubuntu default. That means your allow rules cover both address families automatically, which is what you want on a modern VPS with an IPv6 address.


A Firewall Is One Layer, Not the Whole Wall

UFW controls which ports are reachable; it does nothing about who connects to the ports you leave open. A firewall and SSH hardening solve different problems and belong together: the firewall closes unused ports, key-only SSH secures the one port you must keep open, and Fail2ban bans hosts that abuse the services behind it.

Set default-deny, allow only what you serve, rate-limit SSH, and verify with ufw status verbose every time you change a rule. That discipline shrinks your VPS's attack surface to the handful of ports you actually intend to expose — and nothing more.

Posts in this series