How to Manage a Firewall on Linux: iptables, firewalld, and What’s Actually Modern in 2026
A misconfigured firewall is one of the fastest ways to either get breached or lock yourself out of your own server — sometimes both in the same week. Linux gives you several tools to manage traffic filtering, and they’ve evolved significantly over the years: iptables, then firewalld, and now nftables underneath most of it. This guide walks through how each one works, when to use which, and the practical commands you’ll actually reach for.
Quick Answer
Table of Contents
- The Three Layers: nftables, iptables, and firewalld
- Managing a Firewall with iptables
- The Problem with iptables
- Managing a Firewall with firewalld
- Understanding firewalld Zones
- Creating and Customizing Zones
- firewalld vs. iptables: Side by Side
- Best Practices for Firewall Management
- Conclusion
The Three Layers
Before choosing a tool, it helps to know how they relate to each other, because a lot of confusion comes from treating them as competitors instead of layers.
nftables is the actual kernel-level packet-filtering engine — the successor to the older Netfilter modules that iptables used to talk to directly. iptables (specifically the modern iptables-nft variant) is now largely a compatibility layer: when you type an iptables command, it’s frequently translated into nftables rules behind the scenes rather than using the old kernel path. firewalld sits a level above both, as a policy manager that organizes rules into human-readable zones and services, and applies them through whichever backend — nftables or iptables — your distribution is configured to use.
The practical takeaway: iptables is officially in legacy maintenance mode. It still receives security fixes, and you’ll encounter it constantly on older systems, but new deployments are steered toward nftables, typically managed through firewalld rather than raw syntax.
Managing a Firewall with iptables
At its core, iptables works by matching packets against rules and taking an action — accept, drop, or reject. A basic rule to allow inbound traffic on a specific port looks like this:
iptables -A INPUT -p tcp --dport PORT -j ACCEPT
This appends (-A) a rule to the INPUT chain, matching TCP traffic on the given port, and jumps (-j) to ACCEPT if it matches.
Custom Chains
Beyond the default INPUT, OUTPUT, and FORWARD chains, iptables lets you create your own chains to organize rules logically — useful when you want to separate traffic handling by interface, such as internal versus external networks:
iptables -N INTERNAL
iptables -N EXTERNAL
iptables -A INPUT -i eth1 -j INTERNAL
iptables -A INPUT -i eth0 -j EXTERNAL
Here, traffic arriving on eth1 gets evaluated against the INTERNAL chain, and traffic on eth0 against EXTERNAL — letting you apply very different rule sets depending on which interface the packet came in on.
You can also match on more specific criteria, including connection state, which is essential for allowing return traffic on established connections:
iptables -A INPUT -p tcp -s 192.168.1.100 -d 8.8.8.8 --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
The Problem with iptables
Raw iptables has three well-known pain points that pushed the ecosystem toward better tooling:
- Rules are static. Every rule you add exists only as a line in a ruleset — there’s no built-in concept of “trust levels” or reusable policy groups.
- Changes often require a full restart. Depending on how rules are structured, applying a new configuration can mean flushing and reloading the entire firewall rather than a targeted update.
- Restarts break active state tracking. Because
iptablesrelies on connection tracking (conntrack) to allow established traffic through, a full restart can drop that state table, severing existing connections — a serious problem on a production server with live sessions.
These limitations are exactly what firewalld was built to solve.
Alt text: Conceptual firewall protecting a network from external traffic
Managing a Firewall with firewalld
firewalld manages traffic through services and zones instead of raw packet rules, and — critically — it applies changes dynamically, without dropping active connections or requiring a restart.
Basic service and port management:
firewall-cmd --zone=public --permanent --add-service=ssh
firewall-cmd --zone=public --permanent --add-port=22/tcp
firewall-cmd --reload
The --permanent flag writes the rule to persistent configuration; without it, changes only apply to the current runtime session and disappear on reboot. --reload applies the permanent configuration to the active runtime.
Common Configuration Commands
sudo systemctl enable firewalld # start firewalld automatically on boot
sudo systemctl start firewalld # start it now
sudo firewall-cmd --timeout=60 # apply a rule temporarily, for 60 seconds
sudo firewall-cmd --get-services # list all services firewalld knows about
sudo firewall-cmd --list-services # list services enabled in the current zone
sudo firewall-cmd --get-ports # list all recognized port definitions
sudo firewall-cmd --list-ports # list ports enabled in the current zone
Understanding firewalld Zones
Zones are the core concept that makes firewalld more manageable than raw iptables at scale. Each zone represents a different level of trust, and you assign network interfaces or source IP ranges to the zone that matches how trusted that traffic should be.
man firewalld.zones # detailed zone documentation
firewall-cmd --get-default-zone # see which zone is active by default
firewall-cmd --list-all-zones # see full configuration for every zone
Built-In Zones
| Zone | Behavior |
|---|---|
drop | Drops all incoming traffic with no response at all |
block | Drops traffic but replies with an ICMP host-prohibited message |
external | For external-facing networks, with masquerading (NAT) enabled |
dmz | For hosts in a DMZ, with limited access back to internal machines |
public | For untrusted public networks — the common default |
work / home / internal | Progressively more trusted internal-style zones |
trusted | Accepts all network connections — use sparingly |
Creating and Customizing Zones
Beyond the built-in options, you can define your own zones for specific scenarios — a coffee-shop Wi-Fi profile for a laptop, for example:
firewall-cmd --new-zone=coffeeshop --permanent
firewall-cmd --delete-zone=coffeeshop --permanent
Zones become genuinely useful once you attach source-based rules to them, so specific traffic only gets the zone’s rules if it actually matches:
firewall-cmd --permanent --zone=coffeeshop --add-source=192.168.10.0/24
firewall-cmd --permanent --zone=coffeeshop --add-service=http
firewall-cmd --set-default-zone=coffeeshop
firewall-cmd --reload
To review the final configuration for a specific zone:
firewall-cmd --list-all --zone=coffeeshop
firewalld can also manage NAT (network address translation) through the masquerade option on a zone, which is commonly used on gateway or router-style systems that forward traffic between an internal network and the internet.
firewalld vs. iptables: Side by Side
The same outcome — allowing SSH traffic — looks very different depending on the tool:
# firewalld
firewall-cmd --zone=public --permanent --add-service=ssh
# iptables
iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
The firewalld version is shorter and self-documenting — anyone reading it immediately knows the intent. The iptables version gives you granular, low-level control over exactly how the match happens, but requires you to already understand connection states and chain structure to write it correctly. Neither is “wrong” — they serve different situations. firewalld is generally the better fit for standard servers with several distinct trust zones (LAN, WAN, VPN) and for teams automating changes through an API, while writing raw rules directly still makes sense on minimal, resource-constrained systems like containers or IoT devices where every extra dependency matters.
Best Practices for Firewall Management
- Default to deny. Start from a policy that blocks everything, then explicitly allow only the services and ports you need — rather than starting open and trying to close gaps later.
- Use zones deliberately. Don’t put every interface in
trustedfor convenience; match the zone to the actual trust level of the network it represents. - Prefer
firewalldon modern systems. Its dynamic rule application avoids the dropped-connection problem that plagues fulliptablesrestarts. - Know your backend. On current RHEL, Fedora, and Ubuntu releases,
firewalldis generally running on top ofnftables, so if you need to debug at the packet level,nft list rulesetwill show you what’s actually being enforced. - Persist your changes. Always pair test rules with the
--permanentflag once confirmed, or they’ll vanish on the next reboot. - Log and monitor drops. Use
iptables -L -v -norfirewall-cmd --list-allregularly to confirm the running configuration matches what you intended — misconfigured rules are one of the most common causes of unexpected outages.
Conclusion
Linux firewall management has moved through three overlapping generations: iptables as the long-standing, still-functional but now legacy tool; firewalld as the modern, zone-based manager most administrators should reach for day-to-day; and nftables as the kernel engine now running underneath most of it. You don’t need to master all three at once — start by getting comfortable with firewalld zones and services, since that covers the vast majority of real-world server hardening needs, and dig into raw iptables or nft syntax only when you need that level of control.
Want more hands-on Linux guides like this?
Subscribe to the GEANTECHNOLOGY newsletter for weekly tutorials on networking, cybersecurity, and server administration — or take the next step and secure your infrastructure further.