A firewall is your Linux system’s first line of defense against unauthorized access, malicious traffic, and cyber attacks. Whether you’re running a personal desktop, a home server, or a cloud-based production environment, setting up a properly configured firewall is one of the most essential security practices you can implement.
This guide walks you through three different approaches to setting up a simple yet effective firewall on Linux:
- UFW (Uncomplicated Firewall) – Perfect for beginners and Ubuntu/Debian users
- firewalld – Ideal for RHEL/CentOS/Fedora users and those who prefer a zone-based approach
- nftables – For advanced users who want direct control over Netfilter (the modern replacement for iptables)
By the end of this guide, you’ll have a working firewall that protects your system while allowing legitimate traffic like SSH, web browsing, and application-specific ports. All commands are tested on Ubuntu 24.04, Debian 12, Rocky Linux 9, and Fedora 40.
What You’ll Learn:
- The difference between stateful and stateless firewalls
- How to allow and block specific ports and IP addresses
- How to make your firewall rules persistent across reboots
- Common troubleshooting techniques
Let’s dive in and secure your Linux system in under 10 minutes.
Table of Contents
- Understanding Linux Firewall Basics
- Prerequisites
- Method 1: Setting Up UFW (Ubuntu/Debian)
- Method 2: Setting Up firewalld (RHEL/CentOS/Fedora)
- Method 3: Setting Up nftables (Advanced)
- Making Your Firewall Persistent
- Testing and Verifying Your Firewall
- Common Firewall Rules Examples
- Troubleshooting
- Conclusion
1. Understanding Linux Firewall Basics
Before we start configuring, let’s understand what a firewall does and how Linux handles it.
What is a Firewall?
A firewall is a network security system that monitors and controls incoming and outgoing network traffic based on predefined security rules. Think of it as a security guard at the entrance of your server:
- Incoming traffic – Packets trying to reach your system from the internet or local network
- Outgoing traffic – Packets leaving your system to other computers
- Forwarded traffic – Packets passing through your system (if acting as a router)
Stateful vs. Stateless Firewalls
| Type | Description | Example |
|---|---|---|
| Stateful | Tracks active connections and automatically allows reply traffic | UFW, firewalld, nftables (default) |
| Stateless | Treats each packet independently without connection tracking | Legacy iptables (without conntrack) |
Modern Linux firewalls use stateful packet inspection, meaning once you allow an outgoing connection (e.g., your server requests a webpage), the firewall automatically allows the returning response.
The Netfilter Framework
All Linux firewall solutions ultimately communicate with Netfilter – the packet filtering subsystem built directly into the Linux kernel. The tools we’ll use (UFW, firewalld, nftables) are just user-friendly interfaces to Netfilter.
2. Prerequisites
Before setting up your firewall, ensure you have:
System Requirements
- A Linux distribution (Ubuntu 20.04+, Debian 11+, RHEL 8+, Rocky Linux 8+, AlmaLinux 8+, Fedora 36+)
- Root access or sudo privileges
- Basic familiarity with the command line
⚠️ Critical Warning for Remote Servers
If you are configuring a firewall on a remote server (VPS, cloud instance, etc.), always ensure you have out-of-band access (console via hosting provider) or keep your SSH session active while testing. Locking yourself out of a remote server requires manual intervention from your hosting provider.
Check Your Current Firewall Status
# Check if any firewall is already running
sudo ufw status
sudo firewall-cmd --state
sudo nft list ruleset
# Check open ports (before firewall)
sudo ss -tlnp
sudo netstat -tlnp3. Method 1: Setting Up UFW (Ubuntu/Debian)
UFW (Uncomplicated Firewall) is the default firewall management tool on Ubuntu and Debian. It’s designed to be user-friendly while still powerful enough for most use cases.
Step 1: Install UFW
Most Ubuntu/Debian installations include UFW by default. If not, install it:
sudo apt update
sudo apt install ufw -yStep 2: Set Default Policies
Default policies determine what happens to traffic that doesn’t match any specific rule.
# Deny all incoming traffic by default
sudo ufw default deny incoming
# Allow all outgoing traffic by default (system can initiate connections)
sudo ufw default allow outgoing
# Deny all forwarded traffic (unless your system is a router)
sudo ufw default deny routedStep 3: Allow Essential Services (Before Enabling!)
This is critical for remote servers – always allow SSH before enabling the firewall:
# Allow SSH (port 22) – CHANGE if you use a custom SSH port
sudo ufw allow ssh
# Or explicitly: sudo ufw allow 22/tcp
# For custom SSH port (e.g., 2222):
sudo ufw allow 2222/tcpStep 4: Allow Additional Services
# Web server traffic
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
# Database access (if needed)
sudo ufw allow 3306/tcp # MySQL
sudo ufw allow 5432/tcp # PostgreSQL
# Allow specific IP to access MySQL (more secure)
sudo ufw allow from 192.168.1.100 to any port 3306
# Allow a range of ports
sudo ufw allow 3000:3005/tcp
# Allow based on application profile (if available)
sudo ufw allow 'Apache Full'
sudo ufw allow 'OpenSSH'To see available application profiles:
sudo ufw app listStep 5: Enable UFW
# Enable the firewall (this activates all rules)
sudo ufw enable
# You'll see: "Command may disrupt existing ssh connections. Proceed with operation (y|n)?"
# Type 'y' and press EnterStep 6: Check Firewall Status
# Verbose status with rule numbers
sudo ufw status verbose numbered
# Example output:
# Status: active
# Logging: on (low)
# Default: deny (incoming), allow (outgoing), deny (routed)
# New profiles: skip
#
# To Action From
# -- ------ ----
# [ 1] 22/tcp ALLOW IN Anywhere
# [ 2] 80/tcp ALLOW IN Anywhere
# [ 3] 443/tcp ALLOW IN AnywhereStep 7: Managing UFW Rules
# Delete a rule by number (find numbers from status command)
sudo ufw delete 2
# Delete by rule specification
sudo ufw delete allow 80/tcp
# Insert rule at specific position
sudo ufw insert 1 deny from 203.0.113.0/24
# Block a specific IP address
sudo ufw deny from 192.168.1.100
# Block a subnet
sudo ufw deny from 192.168.1.0/24
# Reset UFW to default (careful - this removes all rules)
sudo ufw resetComplete UFW Setup Script (Copy-Paste Ready)
#!/bin/bash
# Simple UFW Firewall Setup Script
# Reset to defaults
sudo ufw --force reset
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw default deny routed
# Allow essential services
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Optional: Allow ping (ICMP)
sudo ufw allow proto icmp from any to any
# Enable firewall
sudo ufw --force enable
# Show status
sudo ufw status verbose4. Method 2: Setting Up firewalld (RHEL/CentOS/Fedora)
firewalld is the default firewall management tool on RHEL, CentOS, Rocky Linux, AlmaLinux, and Fedora. It uses a zone-based approach, making it ideal for systems that connect to multiple networks (e.g., laptop with home, office, and public Wi-Fi).
Understanding firewalld Zones
Zones define the level of trust for network connections:
| Zone | Trust Level | Typical Use |
|---|---|---|
drop | Lowest | All incoming packets dropped |
block | Very Low | Reject with ICMP messages |
public | Low | Untrusted public networks (default) |
external | Low | NAT/masquerading for outgoing |
internal | Medium | Trusted internal network |
dmz | Medium | Demilitarized zone for public services |
work | High | Trusted work network |
home | High | Trusted home network |
trusted | Highest | Accept all traffic |
Step 1: Install firewalld
# RHEL/CentOS/Rocky/AlmaLinux
sudo dnf install firewalld -y
# Ubuntu/Debian (less common but available)
sudo apt install firewalld -yStep 2: Start and Enable firewalld
# Start the service
sudo systemctl start firewalld
# Enable to start on boot
sudo systemctl enable firewalld
# Check status
sudo systemctl status firewalldStep 3: Check Default Zone
# Check default zone (usually "public")
sudo firewall-cmd --get-default-zone
# List all available zones
sudo firewall-cmd --get-zones
# See active zones and their rules
sudo firewall-cmd --get-active-zonesStep 4: Configure Basic Rules
# Allow SSH (essential for remote servers)
sudo firewall-cmd --add-service=ssh --permanent
# Allow web traffic
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
# Or allow by port number
sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --add-port=3000-3010/tcp --permanentStep 5: Reload and Verify
# Reload to apply permanent rules
sudo firewall-cmd --reload
# Check all rules in default zone
sudo firewall-cmd --list-all
# Example output:
# public (active)
# target: default
# icmp-block-inversion: no
# interfaces: eth0
# sources:
# services: ssh http https
# ports: 8080/tcp
# protocols:
# forward: no
# masquerade: no
# rich-rules:Step 6: Advanced firewalld Management
# Block a specific IP address
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="203.0.113.50" drop' --permanent
# Allow a specific IP to access SSH only
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="ssh" accept' --permanent
# Allow a subnet
sudo firewall-cmd --add-source=192.168.1.0/24 --zone=internal --permanent
# Change default zone for an interface
sudo firewall-cmd --change-interface=eth0 --zone=internal --permanent
# Allow ICMP (ping) – enabled by default, disable if needed
sudo firewall-cmd --remove-icmp-block=echo-request --permanent
# Block ICMP
sudo firewall-cmd --add-icmp-block=echo-request --permanent
# Reload after changes
sudo firewall-cmd --reloadComplete firewalld Setup Script (RHEL/CentOS/Fedora)
#!/bin/bash
# Simple firewalld Setup Script
# Reset to default
sudo systemctl start firewalld
sudo firewall-cmd --runtime-to-permanent
sudo firewall-cmd --reload
# Set default zone to public
sudo firewall-cmd --set-default-zone=public
# Remove all existing services from public zone
sudo firewall-cmd --zone=public --remove-service=dhcpv6-client --permanent
# Add essential services
sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
# Reload and verify
sudo firewall-cmd --reload
sudo firewall-cmd --list-all5. Method 3: Setting Up nftables (Advanced)
nftables is the modern replacement for iptables and is now the default firewall framework in most Linux distributions. It provides a unified syntax for both IPv4 and IPv6 with better performance.
Note: This method is for advanced users. Beginners should use UFW or firewalld.
Step 1: Install nftables
# Ubuntu/Debian
sudo apt install nftables -y
# RHEL/CentOS/Fedora
sudo dnf install nftables -yStep 2: Create a Basic Configuration
Create /etc/nftables.conf:
#!/usr/sbin/nft -f
# Flush existing rules
flush ruleset
# Create table for IPv4 and IPv6 (inet family)
table inet filter {
# Input chain – incoming traffic
chain input {
type filter hook input priority filter; policy drop;
# Allow loopback
iif lo accept
# Allow established/related connections
ct state established,related accept
# Allow ICMP (ping)
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# Allow SSH
tcp dport 22 accept
# Allow HTTP/HTTPS
tcp dport { 80, 443 } accept
# Log dropped packets
log prefix "nftables INPUT drop: " counter drop
}
# Forward chain – routed traffic (drop by default)
chain forward {
type filter hook forward priority filter; policy drop;
}
# Output chain – outgoing traffic (allow by default)
chain output {
type filter hook output priority filter; policy accept;
}
}
Step 3: Load and Enable nftables
# Load the configuration
sudo nft -f /etc/nftables.conf
# Verify rules
sudo nft list ruleset
# Enable and start service
sudo systemctl enable nftables
sudo systemctl start nftablesStep 4: Managing nftables Rules
# List rules with handles (for deletion)
sudo nft list ruleset -a
# Add rule to allow specific IP
sudo nft add rule inet filter input ip saddr 192.168.1.100 accept
# Insert rule at position 2
sudo nft insert rule inet filter input position 2 ip saddr 10.0.0.0/8 drop
# Delete rule by handle (find handle with -a flag)
sudo nft delete rule inet filter input handle 5
# Flush all rules (careful!)
sudo nft flush ruleset
# Save current rules to config
sudo nft list ruleset > /etc/nftables.conf6. Making Your Firewall Persistent
Firewall rules are stored in memory and lost on reboot unless saved.
For UFW
# UFW rules are automatically persistent when enabled
# To ensure service starts at boot:
sudo systemctl enable ufwFor firewalld
# firewalld handles persistence automatically with --permanent
# Ensure service is enabled
sudo systemctl enable firewalldFor nftables
# Enable service to load rules on boot
sudo systemctl enable nftables
# Rules are loaded from /etc/nftables.conf at boot
# After making changes, save:
sudo nft list ruleset > /etc/nftables.conf7. Testing and Verifying Your Firewall
Check Firewall Status
# UFW
sudo ufw status verbose
# firewalld
sudo firewall-cmd --list-all
sudo firewall-cmd --get-active-zones
# nftables
sudo nft list rulesetTest from Another Machine
# Test if SSH is accessible
ssh user@your-server-ip
# Test HTTP with curl
curl -I http://your-server-ip
# Test if a blocked port is actually blocked (from remote)
telnet your-server-ip 25 # Should timeout or refuseCheck Firewall Logs
# UFW logs
sudo tail -f /var/log/ufw.log
# firewalld logs
sudo journalctl -u firewalld -f
# nftables logs (if configured)
sudo dmesg | grep -i "nftables"
sudo journalctl -k | grep -i "nftables"8. Common Firewall Rules Examples
Web Server Configuration
Allow only HTTP, HTTPS, and SSH, block everything else.
UFW:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enablefirewalld:
sudo firewall-cmd --set-default-zone=public
sudo firewall-cmd --remove-service=dhcpv6-client --permanent
sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reloadDatabase Server Configuration
Allow SSH and database port only from specific IPs.
UFW:
sudo ufw default deny incoming
sudo ufw allow from 10.0.0.0/8 to any port 22
sudo ufw allow from 192.168.1.0/24 to any port 5432
sudo ufw enablefirewalld:
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="ssh" accept' --permanent
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="5432" protocol="tcp" accept' --permanent
sudo firewall-cmd --reloadRate Limiting (Prevent Brute Force)
Limit SSH connection attempts to 3 per minute.
UFW:
sudo ufw limit sshfirewalld:
sudo firewall-cmd --add-rich-rule='rule family="ipv4" service name="ssh" limit value="3/m" accept' --permanent
sudo firewall-cmd --reloadAllow Only Outbound DNS
UFW:
sudo ufw default deny outgoing
sudo ufw allow out 53/tcp
sudo ufw allow out 53/udp
sudo ufw allow out 22/tcp # If you need SSH out9. Troubleshooting
Problem 1: Locked Out of Remote Server
Prevention: Always add SSH allow rule before enabling firewall.
Recovery (if you have console access):
# Via hosting provider's console or IPMI
sudo ufw disable
# or
sudo systemctl stop firewalld
# or
sudo nft flush rulesetProblem 2: Application Not Accessible
# Check if firewall is blocking
sudo ufw status verbose
sudo firewall-cmd --list-all
sudo nft list ruleset
# Temporarily disable to test (DANGER - only for testing)
sudo ufw disable
sudo systemctl stop firewalld
# Check if application is listening on correct interface
sudo ss -tlnp | grep <port>Problem 3: Rules Disappeared After Reboot
# UFW – ensure enabled
sudo ufw status # Should show "active"
# firewalld – check service
sudo systemctl is-enabled firewalld
# nftables – check service and config
sudo systemctl status nftables
sudo nft list rulesetProblem 4: Docker Conflicts with Firewall
Docker manipulates iptables directly. Solutions:
# Option 1: Restart firewall after Docker
sudo systemctl restart docker
sudo ufw reload # or sudo systemctl restart firewalld
# Option 2: Configure Docker to respect firewall rules
# Edit /etc/docker/daemon.json
{
"iptables": false
}10. Conclusion
Setting up a simple firewall on Linux is one of the most important security steps you can take. Whether you choose UFW (Ubuntu/Debian), firewalld (RHEL/Fedora), or nftables (advanced), the principles remain the same:
- Deny all incoming traffic by default
- Allow only necessary services (SSH, HTTP, HTTPS)
- Allow outgoing traffic (or restrict if needed)
- Make rules persistent across reboots
- Test thoroughly before relying on the firewall
Quick Reference: Which Tool Should You Use?
| Your Situation | Recommended Tool |
|---|---|
| Ubuntu/Debian desktop or server | UFW (simplest) |
| RHEL/CentOS/Rocky/AlmaLinux | firewalld (default) |
| Fedora Workstation | firewalld |
| Advanced user needing precise control | nftables |
| Cloud server (AWS, DO, Vultr) | UFW or firewalld |
| Container/Kubernetes environment | nftables or Cilium |
Final Checklist
- [ ] Firewall is active and enabled at boot
- [ ] SSH port is allowed (critical for remote servers)
- [ ] Default incoming policy is DENY/REJECT/DROP
- [ ] Required application ports are explicitly allowed
- [ ] Tested from external connection
- [ ] Rules are persistent (survive reboot)
Your Next Steps: Once your basic firewall is working, consider exploring more advanced features:
- IP whitelisting/blacklisting
- Rate limiting for brute force protection
- Port knocking for SSH
- Integration with fail2ban
Found this guide helpful? Bookmark it for future reference or share it with your team. For more Linux security guides, check out our other tutorials on SSH hardening, intrusion detection, and system monitoring.