nftables is the modern replacement for iptables, ip6tables, arptables, and ebtables, and it ships in the RHEL 8 kernel by default. It uses a single unified rule language, supports atomic rule set replacement, and performs better at scale due to its netlink-based architecture. While firewalld can use nftables as its backend, many system administrators prefer to manage nftables rules directly for precise control. This tutorial walks through checking the nftables version, editing the default configuration file, creating tables and chains, adding practical firewall rules, persisting the configuration, and understanding how to safely co-exist with firewalld when needed.

Prerequisites

  • RHEL 8 server with root or sudo access
  • An active SSH session with a console fallback open before modifying firewall rules
  • Basic understanding of network filtering concepts (tables, chains, hooks, priorities)
  • firewalld stopped or managed separately — do not run raw nftables rules while firewalld is active unless you understand the interaction

Step 1 — Verify nftables Availability

Check that the nft binary is installed and the kernel module is loaded.

nft --version
lsmod | grep nf_tables

On a default RHEL 8 installation, nft is available without any additional package installation. If it is missing for any reason, install it with sudo dnf install -y nftables.

Step 2 — Stop firewalld to Avoid Conflicts

When writing raw nftables rules, stop firewalld so its auto-generated chains do not overlap with your own.

sudo systemctl disable --now firewalld
sudo systemctl mask firewalld

# Enable the nftables service so it starts on boot
sudo systemctl enable nftables

Masking ensures firewalld cannot be started by other services or administrators accidentally during rule testing.

Step 3 — Create a Table and Base Chains

nftables rules live inside tables. The inet address family covers both IPv4 and IPv6 simultaneously, which is the recommended approach for modern dual-stack servers.

# Create an inet table called filter
sudo nft add table inet filter

# Add an input chain with a drop policy
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'

# Add a forward chain with a drop policy
sudo nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'

# Add an output chain with an accept policy
sudo nft add chain inet filter output '{ type filter hook output priority 0; policy accept; }'

The priority 0 value places these chains at the default netfilter priority. Lower numbers run first. The policy drop on the input chain means any packet that does not match an explicit rule is silently discarded.

Step 4 — Add Firewall Rules

Add rules in the correct order. Rules are evaluated top to bottom within a chain, so allow rules must come before any drop actions they should precede.

# Allow loopback traffic
sudo nft add rule inet filter input iif lo accept

# Allow established and related connections
sudo nft add rule inet filter input ct state established,related accept

# Allow SSH (TCP port 22)
sudo nft add rule inet filter input tcp dport 22 accept

# Allow HTTP (port 80) and HTTPS (port 443)
sudo nft add rule inet filter input tcp dport { 80, 443 } accept

# Allow ICMPv4 ping
sudo nft add rule inet filter input ip protocol icmp icmp type echo-request accept

# Allow ICMPv6 (required for IPv6 neighbour discovery)
sudo nft add rule inet filter input ip6 nexthdr icmpv6 accept

Step 5 — List and Verify the Rule Set

Confirm the full rule set looks correct before saving.

sudo nft list ruleset

The output should show your inet filter table with all three chains and their rules. Verify that your SSH session remains active. If you are locked out, reboot the server — nftables rules added with nft add are not persistent until saved.

# List only the input chain
sudo nft list chain inet filter input

Step 6 — Persist the Rule Set via /etc/nftables.conf

Export the current live rule set to the system configuration file, then verify the nftables service will load it on boot.

# Write current ruleset to the default config file
sudo nft list ruleset | sudo tee /etc/nftables.conf

# Test that the config file parses without errors
sudo nft -c -f /etc/nftables.conf

# Apply rules from the file to verify it works
sudo nft -f /etc/nftables.conf

# Start and verify the nftables service
sudo systemctl start nftables
sudo systemctl status nftables

The -c flag performs a dry-run syntax check without actually loading the rules, which is useful for validating configuration file edits before applying them. The nftables systemd service reads /etc/nftables.conf on start.

Conclusion

You have verified nftables availability on RHEL 8, disabled firewalld to take direct control, created an inet filter table with drop-default input and forward chains, added rules for loopback, established connections, SSH, HTTP, HTTPS, and ICMP, and persisted the configuration to /etc/nftables.conf for automatic loading at boot. The unified inet address family means these rules protect both IPv4 and IPv6 traffic from a single rule set.

Next steps: Configure iptables Firewall Rules on RHEL 8, Use SELinux with nftables Logging for Intrusion Detection on RHEL 8, and Audit Your Firewall Configuration with Lynis on RHEL 8.