In plain words
A firewall is the guard at the entrance of a building: by default it lets nobody in, and you give it a precise list ("the postman may come in through door 443", "the neighbours on the local network may ring at door 445"). nftables, ufw and firewalld are three different guards who understand the same list written in three languages. The tool writes that list for you in your distribution's language.
Which one to choose?
| Tool | Distributions | Principle |
|---|---|---|
| nftables | All; Debian's default tool | A /etc/nftables.conf file read at boot. The most precise, and the most readable once you are used to it. |
| ufw | Ubuntu, Debian (to install) | Short commands that write nftables rules for you. Ideal for a simple server. |
| firewalld | Fedora, RHEL, AlmaLinux, Rocky, openSUSE | Zones and named services, changed on the fly with firewall-cmd. |
Use only one at a time: two active managers step on each other's toes and the result becomes unpredictable.
Applying without locking yourself out
- Open two SSH sessions: one to apply the rules, the other stays open as a fallback.
- Check that the SSH rule is in what you are about to apply, with the right port.
- Apply, then open a third connection from your workstation. If it works, all is well; otherwise, fix it from the fallback session.
- On a VPS, find the provider's web console before you start: it gives access to the machine even when the network is closed.
Docker bypasses ufw and firewalld
When a container publishes a port (ports: "8080:80"), Docker adds its own rules, which are checked before those of ufw and firewalld. So the port is reachable even if the firewall does not open it. Two simple fixes: publish the port on a specific address ("192.168.1.10:8080:80") or on 127.0.0.1 behind a reverse proxy, or publish nothing at all and use an internal Docker network. The docker-compose generator already applies this principle.
Checking what is really open
From the server, list the listening ports; from another machine, check what really answers:
sudo ss -tulpn # listening ports and their programs
sudo nft list ruleset # all active rules (nftables, ufw, firewalld, Docker)
nmap -Pn 192.168.1.10 # from another machine: reachable ports
A port that is listening but missing from the nmap result is properly filtered. A port visible in nmap that you did not open often comes from a Docker container.
To go further: Securing SSH access completes the firewall with key-based authentication, and Understanding firewalls explains state, default policy and filtering.