A firewall is the first line of defence for any internet-facing server. On Linux, firewalld provides a friendly, zone-based front end over the kernel's nftables/iptables backend, letting you change rules at runtime without dropping established connections. This guide shows you how to install and configure firewalld on Ubuntu 22.04 or 24.04 LTS, ideal when you are hardening a managed VPS or cloud server before exposing it to the public internet.
Prerequisites
- An Ubuntu 22.04/24.04 server with a non-root user that has
sudoprivileges. - SSH access. Keep your current SSH session open while you configure the firewall, so you can recover if you lock yourself out.
Step 1 — Install and start firewalld
Ubuntu ships with ufw by default, not firewalld. If ufw is active, disable it first to avoid two managers fighting over the same backend:
sudo systemctl disable --now ufw
Then install and enable firewalld:
sudo apt update
sudo apt install firewalld -y
sudo systemctl enable --now firewalld
Confirm it is running:
sudo firewall-cmd --state
You should see running.
Step 2 — Understand zones
firewalld groups rules into zones, each representing a trust level for a network connection. Your active zone decides which traffic is allowed. The most common zones are:
| Zone | Typical use |
|---|---|
drop |
Drops all incoming traffic, no reply (most restrictive) |
block |
Rejects incoming with an ICMP message |
public |
Default for untrusted networks; only chosen services allowed |
internal |
Trusted internal networks |
trusted |
Allows all traffic (least restrictive) |
Check the default and active zones:
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones
On a fresh server the default is usually public, which is the right choice for a public-facing VPS. See everything currently allowed in it:
sudo firewall-cmd --zone=public --list-all
Step 3 — Runtime vs permanent: the key concept
Every change is applied to the runtime configuration by default, which is lost on reload or reboot. To make a change survive, add --permanent. Permanent changes do not take effect until you reload.
The safe workflow is: test in runtime, then persist. The cleanest way to persist a working runtime setup is:
sudo firewall-cmd --runtime-to-permanent
Or add --permanent to each rule and then reload:
sudo firewall-cmd --reload
--reload re-reads permanent rules without dropping established connections — your SSH session stays alive.
Step 4 — Allow SSH before anything else
Before tightening the firewall, make sure SSH is allowed, or your next reconnect will fail. The ssh service covers the default port 22:
sudo firewall-cmd --permanent --zone=public --add-service=ssh
If you run SSH on a custom port (for example 2222), open that port instead:
sudo firewall-cmd --permanent --zone=public --add-port=2222/tcp
Apply it:
sudo firewall-cmd --reload
Step 5 — Open services for your applications
firewalld knows hundreds of predefined services by name. List them with firewall-cmd --get-services. For a typical web server, allow HTTP and HTTPS:
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
If you host business email on the same machine, open the relevant submission and IMAP ports:
sudo firewall-cmd --permanent --zone=public --add-service=smtp-submission
sudo firewall-cmd --permanent --zone=public --add-service=imaps
sudo firewall-cmd --reload
To open an arbitrary port not covered by a named service, use --add-port with the protocol:
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
sudo firewall-cmd --reload
Step 6 — Removing rules
To close a service or port, swap add for remove and reload:
sudo firewall-cmd --permanent --zone=public --remove-service=http
sudo firewall-cmd --permanent --zone=public --remove-port=8080/tcp
sudo firewall-cmd --reload
Step 7 — Rich rules for fine-grained control
Rich rules let you express conditions that simple services cannot — for example, restricting access by source IP. To allow SSH only from your office network and reject it everywhere else, first remove the broad ssh service, then add a rich rule:
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
sudo firewall-cmd --permanent --zone=public \
--add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
sudo firewall-cmd --reload
Test the new rule in runtime first (without --permanent) and confirm you can still connect from your allowed IP before persisting it. You can also rate-limit a service to slow down brute-force attempts:
sudo firewall-cmd --permanent --zone=public \
--add-rich-rule='rule service name="ssh" limit value="5/m" accept'
Step 8 — Verify and audit
List the final state of your zone to confirm everything is as intended:
sudo firewall-cmd --zone=public --list-all
Check the permanent configuration for syntax errors:
sudo firewall-cmd --check-config
Recovering from a lockout
If a change cuts off your access, the runtime change disappears on reboot — so a hard reboot from your provider's console restores the previous permanent state. Better still, work over a console session your provider gives you, or set up a timed rollback before risky changes. This is exactly where running on a managed cloud platform helps: you keep out-of-band console access and snapshots so a misconfigured rule never becomes a dead server.
Conclusion
You now have a working firewalld setup: zones for trust levels, named services and ports for your apps, rich rules for source-based restrictions, and a safe runtime-to-permanent workflow. Keep the rule set minimal — open only what you need — and re-audit it whenever you add a new service.
Want this hardening on infrastructure that stays inside the Kingdom, with PDPL/NCA-aligned data residency and local Arabic support? Spin up a VPS or cloud server with Skyline Cloud and apply these steps in minutes. Create your account and get started.
Comments
0 total · 0 threads