Firewall Basics: ufw, firewalld, and iptables
Open the ports your server needs, block the rest, and never lock yourself out — with ufw on Ubuntu, firewalld on RHEL, or raw iptables underneath.
A firewall controls which network traffic is allowed in and out of your server. Different distributions ship different front-ends: Ubuntu/Debian typically use ufw, while RHEL/CentOS/Rocky/AlmaLinux use firewalld. Both sit on top of the kernel's iptables (or the newer nftables). Keep the commands straight — a ufw command won't work on a firewalld box, and vice versa.
ufw (Ubuntu / Debian)
ufw stands for "Uncomplicated Firewall," and the syntax lives up to the name:
sudo ufw allow 22/tcp # Allow port 22 (SSH)
sudo ufw allow OpenSSH # Same thing, by application name
sudo ufw allow 80,443/tcp # Allow HTTP and HTTPS
sudo ufw deny 23 # Block port 23 (Telnet)
sudo ufw enable # Turn the firewall on
sudo ufw status verbose # Show rules and default policies
sudo ufw delete allow 80 # Remove a rule
By default ufw denies incoming and allows outgoing traffic, so once it's enabled, any port you haven't explicitly allowed is blocked.
firewalld (RHEL / CentOS / Rocky / Alma)
firewalld is built around zones and services, and it separates runtime rules from permanent ones:
sudo firewall-cmd --add-service=http --permanent # Permanently allow the HTTP service
sudo firewall-cmd --add-port=8080/tcp --permanent # Permanently allow port 8080
sudo firewall-cmd --reload # Reload so permanent rules take effect
sudo firewall-cmd --list-all # List every rule in the active zone
The key gotcha: changes made with --permanent are written to disk but do not apply until you run --reload. Changes made without --permanent apply instantly but vanish on reboot. To get both, add a runtime rule first to confirm it works, then re-add it with --permanent to make it stick.
iptables fundamentals
iptables is the engine underneath — both ufw and firewalld ultimately generate iptables rules for you. A few concepts are enough to read what's going on:
- Tables: the one you'll deal with most is filter, which allows or blocks traffic.
- Chains: INPUT (traffic to this host), OUTPUT (traffic from this host), and FORWARD (routed traffic).
- Rules: matched top to bottom; the first match wins and applies ACCEPT, DROP, or REJECT.
To inspect the current rules:
sudo iptables -L -n -v # List all chains and rules (-n skips slow DNS lookups)
For day-to-day work, stick with ufw or firewalld and let them manage iptables; hand-writing raw rules is rarely necessary.
Don't lock yourself out
You manage a remote server entirely over SSH. If you enable the firewall before allowing the SSH port, your connection drops immediately and you can't reconnect. Do it in this order:
- Allow the SSH port first (22/tcp by default, or whatever port you moved it to).
- Confirm the rule is in place (ufw status or firewall-cmd --list-all).
- Then run enable or reload.
If you do get locked out, don't panic — log in through your VPS provider's web console, VNC, or rescue mode, which bypasses SSH entirely and lets you fix the firewall rules directly.
Security groups ≠ host firewall
A cloud platform's security group runs at the network layer outside your virtual machine and is managed by the provider, while ufw/firewalld is a host firewall running inside the operating system. For a packet to reach your service, both layers must allow it. When a port seems unreachable, check both sides: a port left closed in the security group can't be fixed by host config alone, and the reverse is equally true.
Summary
Use ufw on Ubuntu and firewalld on RHEL; both are front-ends to iptables. Before changing anything, make sure the SSH port is allowed, then enable or reload — that's how you avoid locking yourself out. And remember that the security group (platform layer) and the host firewall (OS layer) are two separate doors: traffic only gets through when both are open.