Skip to main content

UFW Firewall on a VPS: Configuring the Ubuntu Firewall

Security · 19.04.2026 · 6 min read
Illustration for “UFW Firewall on a VPS: Configuring the Ubuntu Firewall”

UFW (Uncomplicated Firewall) is a front end for iptables/nftables on Ubuntu and Debian that replaces long chains of rules with short commands like ufw allow 22. On a VDS it is your first layer of defense: without it, any port a service listens on is reachable from the internet.

UFW Setup on Ubuntu

The baseline policy is: block everything incoming, allow everything outgoing, then open the ports you actually need one by one.

# Deny everything incoming by default
ufw default deny incoming
ufw default allow outgoing

# Allow SSH — must be done BEFORE enable, or you lose access
ufw allow 22/tcp

# Allow HTTP and HTTPS
ufw allow 80/tcp
ufw allow 443/tcp

# Enable the firewall
ufw enable

# Check the status
ufw status verbose

Reading the ufw status verbose output

Once enabled, the command shows the default policy and the rule list with direction and source:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Lines marked (v6) are the same rules for IPv6. If the server only runs IPv4 and IPv6 is disabled on it, these lines can be ignored — they open nothing if the IPv6 stack is not listening.

Opening Additional Ports

Each service behind an actual port needs to be opened separately — UFW does not know about applications, only about ports and protocols.

# MySQL — local host only, never expose it externally
ufw allow from 127.0.0.1 to any port 3306

# Custom application port (for example Node.js on 8080)
ufw allow 8080/tcp

# Port range, for example for passive FTP
ufw allow 30000:31000/tcp

# Remove a rule added earlier
ufw delete allow 8080/tcp

Restricting Access by IP and Subnet

If an admin panel or a database should only be reachable from your work IP, a rule tied to that address is more reliable than counting on a password alone:

# Allow SSH only from a single address
ufw allow from 203.0.113.10 to any port 22 proto tcp

# Allow the office subnet to the control-panel port
ufw allow from 203.0.113.0/24 to any port 8443 proto tcp

# Explicit block of a specific address (for example, after seeing it in the logs)
ufw deny from 198.51.100.5

Here proto tcp limits the rule to one protocol, to any port sets the destination port, and the address before to is the packet source. Rules are checked in the order they were added, and the first match wins, so a narrower IP-based rule should be added before a general allow 22.

Comparing Ways to Restrict Network Access

MethodWhat it filtersSetup complexityWhen to use
UFWPorts, protocols, IP addressesLow, a command wrapperA single VDS, typical services
iptables directlyFull kernel rule set, NAT, mangleHigh, chain syntaxNon-standard routing, legacy scripts
nftablesSame as iptables, but one syntax for v4/v6MediumModern distributions where iptables is already a wrapper over nft
Provider Security GroupPorts and IPs before the packet reaches the VDSLow, via a panelAn extra layer on top of UFW, not a replacement

On most Ubuntu 22.04+ and Debian 11+ systems, UFW already runs on top of nftables rather than legacy iptables — this does not change the commands, but it explains why the output of iptables -L on such systems can look different from what you expect. More detail is in the article on moving from iptables to nftables.

Common UFW Errors Explained

The error ERROR: Could not delete non-existent rule shows up when the ufw delete command does not match the exact format the rule was added with. If a port was opened as ufw allow 8080 but removed as ufw delete allow 8080/tcp, UFW cannot find an exact match. Check the precise rule entry with ufw status numbered and delete by number instead: ufw delete 3.

Losing SSH access right after ufw enable is the most common accident. If the default deny incoming policy is turned on before a rule for the SSH port is added, the connection simply stops responding: the client gets ssh: connect to host ... port 22: Connection timed out. Access can only be restored through the VDS console in the control panel, where ufw is not in the way — from there you run ufw allow 22/tcp.

The error ERROR: problem running ufw-init when enabling UFW on some minimal VDS images is caused by a missing ip6tables module in the container virtualization kernel. The fix is to turn off IPv6 handling in UFW itself, without touching the kernel: in /etc/default/ufw set IPV6=no and run ufw disable && ufw enable.

When UFW Does Not Fit

  • Docker and similar containerization systems write their own iptables chains directly and can route container ports around UFW rules — a published -p 8080:8080 port sometimes stays reachable even with ufw deny 8080 in place.
  • UFW does not inspect request content (layer 7): filtering by URL, User-Agent, or SQL injection patterns is out of scope — that needs an application-level module or a WAF.
  • With many complex rules involving NAT, port forwarding between interfaces, or policy routing, it is simpler to write them directly in nftables than to force them through UFW syntax.
  • UFW does not replace a dedicated service for protecting a web application against brute-force and bots — that job belongs to Fail2Ban, which blocks IPs based on log analysis rather than a static rule.

Verifying the Result

After configuration, confirm that exactly what you need is open, and nothing more:

  1. Check the numbered rule list: ufw status numbered — every rule should be explainable, and extras are removed with ufw delete NUMBER.
  2. Cross-check the actual listening ports: ss -tulpn — a port may be listening but blocked by UFW, or the reverse, open in UFW but with no service actually running on it.
  3. Check access from outside without closing your current SSH session: open a second session, or use nc -zv your-server-ip 443 from another machine.
  4. Review the block log: tail -f /var/log/ufw.log shows the packets UFW actually dropped — useful when something "does not work" and the firewall is the cause.

If you need finer control over SSH access itself after this — for example, changing the port and disabling password login — that is covered in the article on hardening sshd_config.

Was this article helpful?
← Back to Knowledge Base Ask Support