Set Up UFW Firewall on a New VPS
The safe order: allow SSH first, then enable the firewall, then open only the ports your app actually needs. Avoid the classic mistake that ends your own SSH session mid-command.
UFW — Uncomplicated Firewall — is a friendly wrapper around the kernel's built-in packet filter. It gives you English-like commands (ufw allow 80/tcp) instead of the cryptic iptables/nftables syntax, and on Ubuntu it's already installed. This guide stands up a default-deny firewall with only the ports you need open, in the right order to keep you logged in the whole time.
The classic lockout
Enable a default-deny firewall without allowing SSH first, and your current session ends instantly with no way back in except the rescue console. The step order below exists for exactly this reason — follow it.
Before you start
- Working SSH access with
sudoprivileges. - A second terminal open to test your changes without losing the first session.
- A list of the ports your applications need open (web: 80, 443; mail: 25, 465, 587; etc.).
- On RHEL-family systems, you already have
firewalld— we cover that briefly at the end. UFW is mainly for Debian/Ubuntu.
1Confirm UFW is installed
# Debian / Ubuntu — usually already there sudo apt update && sudo apt install -y ufw # AlmaLinux / Rocky (via EPEL; firewalld is the native choice) sudo dnf install -y epel-release sudo dnf install -y ufw # Check the version sudo ufw version
2Set safe default policies
The base posture is: drop everything inbound that you didn't explicitly allow, but let your server make outbound connections (so package updates, DNS, and apt mirrors still work).
sudo ufw default deny incoming sudo ufw default allow outgoing
These commands don't take effect until you enable UFW in step 4
Setting defaults is a configuration step, not an apply step. You can safely set them, add allow rules, and only then enable the firewall.
3Allow SSH BEFORE enabling the firewall
This is the non-negotiable step. UFW has a profile called OpenSSH that allows TCP/22 with a friendly label; if your SSH is on a different port (see our change-ssh-port guide), allow that port number instead.
# Default port 22 sudo ufw allow OpenSSH # Or a custom port like 2222 sudo ufw allow 2222/tcp comment "ssh (custom port)"
Rate-limit SSH while you're at it
UFW supports a connection-rate limit specifically for brute-force resistance. sudo ufw limit 22/tcp blocks any IP that makes more than 6 connection attempts in 30 seconds. Apply it to your SSH port for a quiet log file.
4Enable the firewall
# UFW will warn that this may disrupt existing connections — type y sudo ufw enable # Verify sudo ufw status verbose
Test SSH from a SECOND terminal right now
Don't close your current session. Open a fresh terminal window and ssh [email protected] — if it connects, you're safe. If it hangs, immediately run sudo ufw disable in your existing session and recheck your allow rule for SSH.
5Open the ports your app actually needs
Add one port at a time, with a comment that explains why. Future-you will thank you when reviewing rules six months from now.
# HTTP and HTTPS for a web server sudo ufw allow 80/tcp comment "http" sudo ufw allow 443/tcp comment "https" # Mail server (SMTP submission, IMAPS, POP3S) sudo ufw allow 25/tcp comment "smtp" sudo ufw allow 587/tcp comment "smtp submission" sudo ufw allow 993/tcp comment "imaps" # WireGuard VPN (default port) sudo ufw allow 51820/udp comment "wireguard" # Game server — example: Minecraft sudo ufw allow 25565/tcp comment "minecraft"
Don't open ports you don't need. A database like MySQL or Postgres almost never needs to listen on a public IP — bind it to 127.0.0.1 and tunnel through SSH or WireGuard instead.
6Restrict admin ports to known source IPs
The single highest-value rule on a small fleet: only your office or VPN IP can reach SSH or your database. Anyone else gets dropped at the firewall, never sees the service exists.
# SSH only from office IP sudo ufw allow from 198.51.100.42 to any port 22 proto tcp comment "office ssh" # Database admin port only from VPN subnet sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp comment "pg admin via vpn" # Multiple IPs — repeat the rule per IP, or use a subnet sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp comment "agency ssh"
Remove the broad SSH rule after adding source-restricted ones
If you have both ufw allow 22 and ufw allow from to any port 22, the broader rule wins and port 22 is open to the world. Delete the broad rule with sudo ufw delete allow 22/tcp after the source-restricted ones are confirmed working.
7Inspect and edit rules
# Verbose status with port numbers and source IPs sudo ufw status verbose # Numbered, so you can delete a specific rule by index sudo ufw status numbered # Sample output: # [ 1] 22/tcp ALLOW IN Anywhere # [ 2] 80/tcp ALLOW IN Anywhere # [ 3] 443/tcp ALLOW IN Anywhere
# By rule spec sudo ufw delete allow 80/tcp # By number (after "ufw status numbered") sudo ufw delete 3 # Reset everything (clears all rules and disables UFW) sudo ufw reset # be very careful — answer y only if you mean it
8Logs: see what's getting blocked
UFW logs every dropped packet to /var/log/ufw.log (and journalctl -k on systemd hosts). This is gold for debugging "why can't this service connect?" and for spotting reconnaissance scans.
# Increase logging detail (low is the default) sudo ufw logging medium # Tail the live drop log sudo tail -f /var/log/ufw.log # Or via journal sudo journalctl -kf | grep UFW
RHEL-family systems: a quick note on firewalld
AlmaLinux, Rocky, and other RHEL-family distros ship with firewalld, not UFW. Same idea, different syntax. The equivalent of the steps above:
# Status sudo firewall-cmd --state # Open SSH first (the zone "public" is the default) sudo firewall-cmd --permanent --add-service=ssh # Open web ports sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https # Open a custom port sudo firewall-cmd --permanent --add-port=2222/tcp # Apply sudo firewall-cmd --reload # Inspect sudo firewall-cmd --list-all
Pairing UFW with fail2ban
UFW is the stationary guard: a packet either matches a rule or it doesn't. For dynamic blocking based on behaviour — banning an IP that fails ten SSH logins in a minute — you want fail2ban alongside it. fail2ban watches log files and inserts temporary deny rules through your firewall when an IP misbehaves, then removes them after the ban window. It's a few lines of config away from a quiet, well-defended SSH port.
Frequently asked questions
Is UFW different from iptables/nftables?
How do I make my rules survive a reboot?
sudo ufw enable marks UFW to start on boot, and rules live in /etc/ufw/user.rules (and friends), reloaded by the ufw.service unit on each boot.My web server is running but the site is unreachable from outside. Why?
sudo ufw status); the web server is bound to 127.0.0.1 only (ss -tlnp shows local addresses); or your VPS provider has a cloud firewall layer outside the OS. We have a cloud firewall in our client area — check that the relevant port is allowed there too.Should I open ICMP (ping)?
/etc/ufw/before.rules. Blocking ping breaks monitoring and diagnostics for almost no security benefit — the host is trivially discoverable by other means.Can VPS-SERVER HOST configure UFW for me?
Want a layered firewall?
Every VPS-SERVER HOST plan can sit behind our managed cloud firewall, which scrubs traffic at the network edge before it reaches your OS-level UFW rules. Two layers, one client area.
Deploy a VPS Open a Ticket