Why configure UFW on your VPS
By default, many VPS images leave services listening and enforce no coherent firewall policy. iptables is powerful but verbose and easy to misconfigure; UFW offers a readable layer on top of it where you reason in terms of "allowed ports" rather than chains and targets. The guiding principle is simple: block all incoming traffic by default and explicitly open only what is necessary (SSH, HTTP, HTTPS). Self-managing your firewall on the VPS, rather than relying solely on any provider-side filtering, gives you a defense in depth that is independent and audited by yourself. It is the first building block of serious hardening.
What UFW lets you do
- Adopt a "deny by default" inbound policy, opening only the ports actually used.
- Rate-limit SSH attempts (
ufw limit) to slow down brute-force attacks. - Restrict a port to a single source IP (your back office, your workstation) in one command.
- Read and audit your rules at a glance with
ufw status numbered, without decoding iptables. - Take advantage of pre-registered application profiles (
ufw app list) for Nginx, OpenSSH, etc. - Log rejected traffic to spot scans and intrusion attempts.
Prerequisites for this deployment
UFW is utterly lightweight: it runs on the most modest of VPSes (1 vCPU, 512 MB of RAM is enough), since it merely generates iptables/nftables rules. You need root or sudo access, the ufw package (present or installable via apt install ufw) and, crucially, to know your SSH port before any change so you don't lock yourself out. No Docker or domain is required here. If your services run in Docker containers, keep in mind that Docker manipulates iptables directly and can bypass UFW: particular care is then required.
Configure UFW step by step
Allow SSH BEFORE any other rule
This is the step that prevents lockout. Even before enabling the firewall, allow your SSH port: sudo ufw allow 22/tcp (or your custom port). Never enable UFW without this rule, at the risk of losing access to your VPS.
Set the default policies
Lay the deny-by-default foundation: sudo ufw default deny incoming and sudo ufw default allow outgoing. All incoming traffic that is not explicitly allowed will now be rejected, while your services can still initiate outgoing connections.
Open the ports of exposed services
Allow only what must be public. For a web server: sudo ufw allow 80/tcp and sudo ufw allow 443/tcp, or directly the profile sudo ufw allow 'Nginx Full'. Leave closed any port that is not meant to be reached from the Internet.
Enable rate limiting on SSH
Replace the simple SSH rule with sudo ufw limit 22/tcp: UFW then temporarily blocks an IP that attempts more than 6 connections in 30 seconds, which greatly slows down brute-force attacks without banning your legitimate access.
Restrict sensitive ports by source IP
For an admin panel or a database, open the port only to your address: sudo ufw allow from 203.0.113.10 to any port 5432 proto tcp. No other source will be able to reach this service, even if it is listening.
Enable UFW and check the rules
Enable the firewall with sudo ufw enable, confirm, then audit everything via sudo ufw status numbered. Keep an SSH session open during the test so you can fix a rule if you cut off your own access.
If your services run in Docker, be aware that Docker inserts its own iptables rules upstream of UFW's: a container started with -p 5432:5432 is reachable from the Internet even if UFW "blocks" port 5432. Two robust remedies: explicitly bind the port to the loopback (-p 127.0.0.1:5432:5432) for services not intended for the public, or install the ufw-docker project which reconciles the Docker and UFW chains. Also enable sudo ufw logging on and monitor /var/log/ufw.log to detect scans before they turn into intrusions.