Linux Firewall Cheat Sheet: ufw and firewall-cmd Side by Side
Allow SSH and a web server, then turn the firewall on:
# ufw (Ubuntu, Mint, Debian if installed)
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status verbose
# firewalld (Fedora, RHEL family, openSUSE)
sudo firewall-cmd --permanent --add-service={ssh,http,https}
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
Both tools are front ends to the kernel's nftables packet filter. ufw is simple and rule-based; firewalld groups interfaces into zones and has a separate runtime and permanent configuration. Use the one your distro ships and never run both at once. The step-by-step setup, including Docker interaction, is in the ufw and firewalld guide.
Allow SSH before enabling or tightening a firewall on a remote machine, and keep your current session open while you test a new login in a second terminal. If you are locked out, you will see Connection timed out rather than "refused".
Install, enable and check status
| Task | ufw | firewalld |
|---|---|---|
| Install | sudo apt install ufw / sudo pacman -S ufw | sudo dnf install firewalld / sudo zypper install firewalld / sudo apt install firewalld |
| Enable now and at boot | sudo ufw enable | sudo systemctl enable --now firewalld |
| Disable | sudo ufw disable | sudo systemctl disable --now firewalld |
| Is it running? | sudo ufw status | sudo firewall-cmd --state |
| Show all rules | sudo ufw status verbose | sudo firewall-cmd --list-all |
| Rules with numbers | sudo ufw status numbered | n/a; sudo firewall-cmd --list-all-zones shows every zone |
| Apply saved config | sudo ufw reload | sudo firewall-cmd --reload |
| Reset to defaults | sudo ufw reset | Remove files in /etc/firewalld/zones/, then reload |
Allow and deny ports and services
| Task | ufw | firewalld |
|---|---|---|
| Allow a TCP port | sudo ufw allow 8080/tcp | sudo firewall-cmd --permanent --add-port=8080/tcp |
| Allow a UDP port | sudo ufw allow 51820/udp | sudo firewall-cmd --permanent --add-port=51820/udp |
| Allow a port range | sudo ufw allow 6000:6010/tcp | sudo firewall-cmd --permanent --add-port=6000-6010/tcp |
| Allow a named service | sudo ufw allow OpenSSH | sudo firewall-cmd --permanent --add-service=ssh |
| List known service names | sudo ufw app list | firewall-cmd --get-services |
| Remove an allowed port | sudo ufw delete allow 8080/tcp | sudo firewall-cmd --permanent --remove-port=8080/tcp |
| Remove a service | sudo ufw delete allow OpenSSH | sudo firewall-cmd --permanent --remove-service=ssh |
| Delete rule by number | sudo ufw delete 3 | n/a (remove by what it allows) |
| Deny a port explicitly | sudo ufw deny 23/tcp | Closed unless allowed; use a rich rule to reject explicitly |
| Rate-limit SSH brute force | sudo ufw limit OpenSSH | Rich rule with limit value="10/m" |
After any --permanent change, run sudo firewall-cmd --reload. ufw rules take effect immediately.
Restrict by source IP or interface
| Task | ufw | firewalld |
|---|---|---|
| Allow everything from one IP | sudo ufw allow from 192.168.1.10 | sudo firewall-cmd --permanent --zone=trusted --add-source=192.168.1.10 |
| Allow a port only from a subnet | sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp | sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept' |
| Block an IP | sudo ufw insert 1 deny from 203.0.113.7 | sudo firewall-cmd --permanent --zone=drop --add-source=203.0.113.7 |
| Rule for one interface | sudo ufw allow in on wg0 to any port 53 | sudo firewall-cmd --permanent --zone=internal --change-interface=wg0 |
| Allow outgoing only on a port | sudo ufw allow out 53 | Outgoing is open by default; use policies to restrict |
Need help writing a CIDR range like 192.168.1.0/24? Use the subnet calculator.
Defaults, zones and logging
| Task | ufw | firewalld |
|---|---|---|
| Default deny incoming | sudo ufw default deny incoming | Default zone public already rejects unlisted ports |
| Default allow outgoing | sudo ufw default allow outgoing | Default behaviour |
| Show default zone | n/a | firewall-cmd --get-default-zone |
| Which zone each interface is in | n/a | firewall-cmd --get-active-zones |
| Change default zone | n/a | sudo firewall-cmd --set-default-zone=home |
| Work in a specific zone | n/a | Add --zone=NAME to any command |
| Turn on logging | sudo ufw logging on (logs to the kernel log, prefix [UFW BLOCK]) | sudo firewall-cmd --set-log-denied=all |
| Read blocked packets | journalctl -k | grep 'UFW BLOCK' | journalctl -k | grep -i 'REJECT\|DROP' |
| Try a rule for 5 minutes | n/a | sudo firewall-cmd --add-port=8080/tcp --timeout=5m |
| Save runtime rules as permanent | n/a (always persistent) | sudo firewall-cmd --runtime-to-permanent |
| Port forwarding | Edit /etc/ufw/before.rules (NAT section) | sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080 |
nftables underneath
| Command | What it does |
|---|---|
sudo nft list ruleset | The real rules the kernel is using, whichever front end wrote them |
sudo nft list tables | Tables present (firewalld uses inet firewalld) |
sudo iptables -L -n -v | Legacy view; on current distros this is iptables-nft translating to nftables |
sudo ss -tulpn | What is actually listening, to compare with what you opened |
sudo systemctl enable --now nftables | Load hand-written rules from /etc/nftables.conf (Debian, Arch) or /etc/sysconfig/nftables.conf (Fedora) |
Common recipes
Fresh Ubuntu server with SSH, HTTP and HTTPS:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit OpenSSH
sudo ufw allow 'Nginx Full' # or: sudo ufw allow 80,443/tcp
sudo ufw enable
Fedora workstation: open a development port for the local network only:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="3000" protocol="tcp" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --list-rich-rules
Check from another machine whether a port is reachable:
nc -zv server.example.com 443
# "succeeded" = open; "refused" = nothing listening; timeout = filtered by a firewall
SSH listens on a custom port (see the SSH server guide):
sudo ufw allow 2222/tcp
sudo firewall-cmd --permanent --add-port=2222/tcp && sudo firewall-cmd --reload
sudo semanage port -a -t ssh_port_t -p tcp 2222 # also needed on SELinux systems
Gotchas
- firewalld runtime vs permanent. A rule without
--permanentvanishes on reload; a rule with only--permanentis not active until you reload. Use both, or--runtime-to-permanent. - Docker bypasses ufw. Published container ports (
-p 8080:80) are opened in Docker's own chains before ufw sees them. Bind to127.0.0.1:8080:80or use theDOCKER-USERchain. - Rule order in ufw. The first matching rule wins. To block an IP that an earlier rule allows, use
ufw insert 1 deny from .... - Refused vs timed out. "Connection refused" means the packet arrived and nothing is listening; a firewall that drops packets causes a timeout, and a reject rule can cause No route to host.
- Cloud firewalls. On AWS, Azure, GCP or a VPS panel, a security group can block traffic before it reaches your host firewall. Open the port in both places.
- IPv6. ufw applies rules to IPv6 when
IPV6=yesin/etc/default/ufw(the default). firewalld zones cover both families unless you usefamily="ipv4"in a rich rule.
Upstream documentation: UFW on help.ubuntu.com, firewalld.org and nftables on the Arch Wiki.
Frequently asked questions
Which firewall does my distro use?
Ubuntu ships ufw (inactive by default). Fedora, RHEL-family and openSUSE use firewalld, which is active by default. Debian and Arch install neither; add ufw or firewalld yourself or write nftables rules.
How do I open a port with firewall-cmd permanently?
Run sudo firewall-cmd --permanent --add-port=8080/tcp and then sudo firewall-cmd --reload. Without --permanent the rule only lasts until the next reload or reboot.
Will enabling ufw lock me out of SSH?
It can, because the default policy denies incoming connections. Run sudo ufw allow OpenSSH (or sudo ufw allow 22/tcp) before sudo ufw enable.
How do I delete a ufw rule?
List rules with numbers using sudo ufw status numbered, then sudo ufw delete N. You can also repeat the original rule with delete in front: sudo ufw delete allow 80/tcp.
Can I use ufw and firewalld together?
No. Both manage the same nftables backend and will fight over the rules. Pick one, and disable the other with systemctl disable --now.
Spotted a mistake or a command that no longer works? Send a correction — pages are re-checked when reports come in.