firewalld Basics on RHEL-Family Linux
A practical introduction to firewalld - zones, services, and ports - with the firewall-cmd commands you need to open ports safely on a new VPS.
4 October 2024 5 min read
Every RHEL-family distribution – AlmaLinux, Rocky Linux, Oracle Linux, VzLinux, Fedora – ships with firewalld enabled by default. If you’ve just provisioned a VPS and can’t reach a service you’ve installed, firewalld blocking the port is one of the first things to check. This guide covers the concepts and the commands you need to manage it confidently.
How firewalld thinks: zones
Unlike a flat list of allow/deny rules, firewalld organises things around zones. A zone is a named set of rules that defines how much a network interface is trusted. Each network interface is assigned to exactly one zone, and the rules for that zone apply to any traffic arriving on it.
Common predefined zones include:
public– for use in untrusted public networks; the default zone on most installs, and almost always the one your VPS’s network interface is intrusted– accepts all traffic; rarely appropriate on a public-facing serverinternal/home/work– for trusted local networksdrop/block– very restrictive, drops or rejects almost everything inbound
On a typical VPS, you’ll do almost all your work in the public zone and never need to touch the others.
Services vs. ports
Within a zone, you can allow traffic two ways:
- Services – predefined, named rules that bundle the correct port(s) and protocol for a common piece of software, e.g. allowing the
sshservice opens TCP port 22. These are just XML definitions under/usr/lib/firewalld/services/, and firewalld ships with definitions for dozens of common services. - Ports – raw port/protocol combinations, e.g.
8080/tcp, for anything that doesn’t have (or doesn’t match) a predefined service.
Using the named service is preferable when one exists, since it’s self-documenting – --add-service=http is clearer than --add-port=80/tcp to anyone reading your config later.
Checking firewalld’s status
Confirm firewalld is running:
sudo firewall-cmd --state
See which zone your interfaces are actually using:
firewall-cmd --get-active-zones
Which will typically show something like:
public
interfaces: eth0
Check which zone is the default (used for interfaces that aren’t explicitly assigned elsewhere):
firewall-cmd --get-default-zone
And see the full rule set for a zone – services, ports, and everything else currently allowed:
firewall-cmd --zone=public --list-all
Output looks something like this:
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
forward: no
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
Note that a fresh AlmaLinux or Rocky Linux VPS typically has ssh allowed by default – that’s what’s kept your existing SSH session working. Don’t remove it without opening another way in first, or you’ll lock yourself out.
Runtime vs. permanent changes – the part that trips people up
This is the single most important thing to understand about firewalld. By default, firewall-cmd changes are runtime only – they take effect immediately, but disappear the next time firewalld restarts or the server reboots.
To make a change stick, add the --permanent flag. But permanent changes don’t take effect immediately – they’re written to the on-disk configuration and only applied on the next reload or restart.
In practice, this means you either:
- Run the command twice – once without
--permanentto apply it now, and once with--permanentto save it for next time, or - Run it once with
--permanent, then runfirewall-cmd --reloadto apply the saved configuration immediately (a reload doesn’t drop existing connections, so it’s safe to run over SSH)
The second approach is generally cleaner and is what we’ll use below.
Worked example: opening port 443 for a web server
Say you’ve just installed nginx and configured TLS, and you need to let HTTPS traffic through. HTTPS is a predefined firewalld service, so use it rather than the raw port number:
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
Verify it’s there:
firewall-cmd --zone=public --list-services
cockpit dhcpv6-client http https ssh
If you’d rather work with the raw port instead (say, for a service without a predefined firewalld entry), the equivalent is:
sudo firewall-cmd --permanent --zone=public --add-port=443/tcp
sudo firewall-cmd --reload
Check open ports the same way:
firewall-cmd --zone=public --list-ports
To remove a rule you no longer need, swap --add- for --remove-, again with --permanent and a reload:
sudo firewall-cmd --permanent --zone=public --remove-service=https
sudo firewall-cmd --reload
Listing available predefined services
To see every service firewalld knows about before deciding whether to use a named service or a raw port:
firewall-cmd --get-services
This returns a long space-separated list including things like http, https, ssh, ftp, mysql, postgresql, and smtp. If the service you need isn’t listed, use --add-port with the correct port and protocol from the software’s own documentation.
A quick reference
| Task | Command |
|---|---|
| Check firewalld is running | firewall-cmd --state |
| Show active zones | firewall-cmd --get-active-zones |
| Show default zone | firewall-cmd --get-default-zone |
| List everything allowed in a zone | firewall-cmd --zone=public --list-all |
| Open a service permanently | firewall-cmd --permanent --zone=public --add-service=https |
| Open a port permanently | firewall-cmd --permanent --zone=public --add-port=443/tcp |
| Apply saved changes | firewall-cmd --reload |
| List available services | firewall-cmd --get-services |
The rule of thumb: only open what you actually need running, use the named service where one exists, and always pair --permanent changes with a --reload so what you tested is actually what survives a reboot.