Security

How to configure the UFW firewall on an Ubuntu VPS

The first thing I do on a fresh Ubuntu VPS is not install any fancy tool. It is enable the firewall. Most Ubuntu images ship with UFW installed but disabled, which means every port is open to the internet until you say otherwise. That is a ticking clock. Configure the UFW firewall on an Ubuntu VPS before you expose any service, and you take away the easiest attack path before it becomes a problem. This guide runs on Ubuntu 24.04 LTS and the newer Ubuntu 26.04 LTS, both of which ship UFW 0.36.x with the nftables backend.

Prerequisites

  • An Ubuntu VPS running Ubuntu 24.04 LTS or 26.04 LTS with a dedicated IPv4.
  • Root access or a sudo user. I assume you already have an SSH session open, because locking yourself out is the classic mistake here.
  • A clear list of the ports your services actually need. If you are unsure, start with SSH and add rules as you go.

Why UFW and why it matters on a fresh VPS

UFW, the Uncomplicated Firewall, is the default frontend for firewall rules on Ubuntu. It sits on top of nftables through the iptables-nft compatibility layer, so you get a clean syntax without touching raw nft rules. The whole job of a firewall is to decide what traffic reaches your services. On a default Ubuntu install every inbound port is open, and a server on a public IPv4 gets probed within minutes of booting.

Here is the mental model I use: deny everything you did not explicitly allow. That is it. A default-deny firewall means an attacker scanning your IPv4 sees no open service they were not meant to see. This is especially valuable on a dedicated server or a VPS that runs multiple services, because a forgotten open port on a test app is no longer silently exposed.

Step 1 - Check the current status before touching anything

Before you enable anything, look at what is already there. The first rule of firewall work is knowing your starting point.

sudo ufw status verbose

On a fresh install you will see Status: inactive. If you see active rules, someone configured it before you, and you should review each one before changing anything.

There is a long-standing warning here: never enable UFW with an SSH rule missing. Keep your SSH session open, add the SSHH rule first, and only then enable the firewall.

Step 2 - Set the default policies

The default policy is what happens to traffic that matches no rule. Set it to deny incoming and allow outgoing, which is the safe baseline for any server that talks out to the internet for updates, DNS, or API calls.

sudo ufw default deny incoming
sudo ufw default allow outgoing

The first command tells UFW to drop any inbound connection that is not explicitly allowed. The second keeps outbound traffic open, so your VPS can still run apt update, reach databases, and answer DNS queries. If you flip the outgoing policy to deny, you have to enumerate every outbound destination, which turns into a maintenance headache and brings no real security gain for most setups.

Step 3 - Allow SSH before you enable the firewall

This is the step that prevents the famous lockout. If you are connected over port 22, allow it before enabling. The safest form uses the service name so UFW resolves the current SSH port.

sudo ufw allow OpenSSH

If you moved SSH to a non-standard port, like the 2200 example in how to change the default SSH port on a VPS, allow that port explicitly instead:

sudo ufw allow 2200/tcp

Use tcp in the rule. SSH does not use UDP, and a rule without a protocol applies to both, which is broader than you need. The port-based rule overrides the service name, so do not add both for the same service, it only clutters the list.

Step 4 - Enable the firewall and verify you are still connected

Now turn it on and confirm the rules are live.

sudo ufw enable

This activates UFW and makes it start on boot through systemd. You will see a warning about the connection, but since SSH is already allowed, the session stays alive.

Verify the state and the exact rules:

sudo ufw status verbose

Expected output shows Status: active, the default policies you set, and a list with OpenSSH ALLOW Anywhere. If you see Status: inactive, the enable failed, check the service log before doing anything else.

systemctl status ufw

Step 5 - Open the ports your services actually use

With the default-deny policy active, every new service needs an explicit rule. Here are the rules I use most often on a VPS:

ServicePortCommand
HTTP (Nginx, Apache, LiteSpeed)80/tcpsudo ufw allow 80/tcp
HTTPS443/tcpsudo ufw allow 443/tcp
SMTP (PowerMTA, Postfix)25/tcpsudo ufw allow 25/tcp
Docker API (bind to localhost only)-no rule, keep it internal
WireGuard51820/udpsudo ufw allow 51820/udp

If you run WordPress on a VPS with LiteSpeed, you only need 80 and 443 exposed. Everything else, the database, Redis, the control panel if any, should stay bound to localhost or a private interface. Do not open a port just because a service listens on it. Open it only when a client outside the server must connect to it.

For a specific source IP, scope the rule. This is the correct way to expose an admin panel or a database to your office only:

sudo ufw allow from 203.0.113.10 to any port 3306 proto tcp

That rule lets exactly one IP reach MySQL, and drops everyone else. This pattern beats opening the port to the whole internet every single time.

Step 6 - Slow down SSH brute force with rate limiting

An SSH service on a public IPv4 collects login attempts constantly. The reliable mitigation is not a complex intrusion detection system, it is rate limiting. You likely already have fail2ban or CrowdSec in place from hardening SSH on a new VPS with keys, ports, fail2ban and CrowdSec, and UFW rate limiting works as a second layer.

sudo ufw limit ssh

Or with the numeric port:

sudo ufw limit 2200/tcp

The limit action accepts a maximum of 6 connections per 30 seconds per IP, and drops the rest. It does not fix bad SSH security on its own, but it massively cuts the noise. Combine it with key-based auth and a non-root user from user and sudo permissions management on a Linux VPS, and the brute force problem mostly disappears.

Step 7 - Manage the rules you no longer need

Firewalls drift. A port you opened for a temporary test stays open for months. I delete rules the moment a service moves or dies.

sudo ufw delete allow 8080/tcp

Deleting mirrors the original rule syntax. If you cannot remember the exact wording you used, get the numbered list and delete by number, which is less error-prone:

sudo ufw status numbered
sudo ufw delete 5

Use status numbered with caution, the numbers shift after a delete, so re-run the command before removing another rule.

Step 8 - Test the firewall from outside

Checking ufw status tells you what the config is, not what the network actually sees. Test from a second machine, your laptop or a friend's server, not from the VPS itself, because a test from inside the server can give false positives when routing and local sockets get involved.

sudo apt install nmap
nmap -Pn your-server-ipv4

Expected output: SSH shows as open, ports you did not allow show as filtered. A filtered port means UFW dropped the packet, which is exactly what you want. If you see an unexpected open port, go back to sudo ufw status numbered and find the rule that allows it.

Troubleshooting - what breaks and how to fix it

The common failures are few, and they are predictable.

Locked out after enabling. If your SSH session still works, you forgot nothing. If it does not, you allowed the wrong port. Reconnect through the provider's console, the KVM or web terminal from the control panel, and run sudo ufw allow OpenSSH immediately. On a thueVPS VPS the web console gives you a root shell without network access, which is the recovery path.

Rules look right but a port is still closed. Check whether another firewall, a cloud security group, or the provider's network layer is filtering first. Run sudo ufw status verbose to confirm the rule exists, then check listeners with ss -tlnp | grep :443 to see if the service itself is bound to the right interface. A service bound to 127.0.0.1 will never answer on the public IPv4 no matter what UFW allows.

Docker ignores UFW rules. Docker writes its own iptables rules and bypasses UFW entirely. Containers that publish ports are visible even when UFW denies them. The reliable fix is to bind containers to localhost with 127.0.0.1:8080:80 and put a reverse proxy in front, which matches the pattern in run multiple sites on one VPS with Docker and Nginx proxy. This is not a UFW bug, it is how Docker's networking works, and you work around it at the compose level.

Why does UFW use nftables instead of iptables?

UFW 0.36.x and newer versions translate every rule into nftables through the iptables-nft compatibility layer. nftables is the modern packet classification framework on Linux, and it replaces the older iptables tooling with a single in-kernel virtual machine. For you as an administrator nothing changes, you still type sudo ufw allow 443/tcp, but the backend is current and maintained. This matters because the old iptables stack is frozen and receives no new features. If you prefer working with nft directly, see set up nftables firewall on a VPS to replace iptables for the raw syntax.

FAQ

How do I enable UFW without locking myself out?

Add an allow rule for SSH before you run sudo ufw enable. Use sudo ufw allow OpenSSH for the default port, or sudo ufw allow 2200/tcp if SSH listens elsewhere. Keep the SSH session open while enabling, and verify with sudo ufw status verbose.

What is the default UFW policy on Ubuntu?

A fresh Ubuntu install has UFW inactive with no default policy set. The recommended baseline is sudo ufw default deny incoming and sudo ufw default allow outgoing, which you set yourself before adding allow rules.

Does UFW block Docker published ports?

No. Docker manages its own iptables rules and bypasses UFW. A published container port is reachable even when UFW denies it. Bind containers to 127.0.0.1 and put a reverse proxy in front to regain control.

How do I delete a UFW rule?

Use sudo ufw delete allow 8080/tcp with the exact rule syntax, or sudo ufw status numbered followed by sudo ufw delete 5 to remove by line number. Numbers shift after each delete, so re-list before removing again.

Is ufw limit enough to stop SSH brute force?

It reduces the attack rate to 6 connections per 30 seconds per IP, but it is not a complete fix. Pair it with key-based authentication, a non-root user, and optionally fail2ban or CrowdSec.

Related articles

Ubuntu VPS 配置 UFW 防火墙要点

为 Ubuntu VPS 启用 UFW 防火墙时,第一步是设置默认拒绝入站、允许出站,并在启用前明确放行 SSH 端口,避免把自己锁在服务器外。规则应只开放业务实际使用的端口,并用 ufw limit 限制 SSH 的暴力破解尝试。Docker 发布端口会绕过 UFW,建议将容器绑定到 127.0.0.1 并在前面加反向代理。每次改动后用 ufw status verbose 验证规则是否生效。

Note: This guide is for general reference. Every system and infrastructure has its own specifics, so test each step in a safe environment and consult a qualified engineer before applying it in production.