# Installing and Using nftables in Linux

`nftables` is a modern packet filtering framework for Linux that replaces iptables. It offers a unified and efficient way to manage firewall rules across multiple protocols like IPv4, IPv6, and ARP, with better performance and a simpler configuration through a single interface instead of multiple tables. This guide installs and configures `nftables` on Linux, sets up basic firewall rules, manages network traffic, and implements common security policies. By the end, you'll have a persistent `nftables` firewall with SSH, HTTP, and HTTPS access rules, NAT/port forwarding, and logging in place, following Linux firewall configuration practices documented in [**Vultr Docs**](https://docs.vultr.com/how-to-install-and-use-nftables-in-linux).

* * *

## Prerequisites

Before you begin, you need to:

*   Have access to a Linux-based instance as a non-root sudo user.
    
*   Have a basic understanding of network protocols and firewall rule management.
    

## `nftables` versus `iptables`

`iptables` is the traditional firewall management tool on Linux, but its limitations include a complex rule structure, performance inefficiencies, and the need for separate tools to handle different protocols (IPv4, IPv6, ARP, and so on). `nftables` was introduced as a replacement, offering improvements in functionality, performance, and usability:

| Feature | `iptables` | `nftables` |
| --- | --- | --- |
| Packet Filtering | Requires multiple tools to manage different protocols (iptables, ip6tables, arptables) | Uses a unified approach with named tables and chains |
| Rule Syntax | Forces users to write complex chain-based rules with multiple entries | Offers a modern, table-based syntax that simplifies rule writing |
| Performance | Processes rules sequentially, which can slow down large rule sets | Uses optimized rule evaluation, reducing overhead |
| Atomic Rule Changes | Requires flushing and reloading entire rule sets for modifications | Supports atomic rule updates, preventing disruption |
| Logging | Uses legacy logging targets (LOG and ULOG frameworks) | Provides built-in logging with advanced tracing options (`meta nftrace`, `log`) |
| Connection Tracking | Requires external `conntrack` modules | Integrates connection tracking directly into the system |
| Multi-protocol Support | Requires different commands for each protocol | Manages all protocols with a single command set |
| Resource Usage | Consumes more memory by duplicating rules | Manages memory efficiently with a unified ruleset |
| Scripting & Automation | Relies on shell scripting and manual rule management | Supports JSON-based rule definition and direct scripting integration |
| Compatibility | Runs on all distributions but follows a legacy architecture | Works natively on modern systems while supporting `iptables` conversion |

## Install `nftables` on Linux

Install `nftables` using the package manager for your Linux distribution.

### On Ubuntu and Debian

**1\. Update the server's package index:**

```console
sudo apt update
```

**2\. Install** `nftables` **using the APT package manager:**

```console
sudo apt install nftables -y
```

### On Rocky Linux and AlmaLinux

**1\. Update the DNF package index:**

```console
sudo dnf update
```

**2\. Install** `nftables`**:**

```console
sudo dnf install -y nftables
```

### On Arch Linux

**1\. Install** `nftables` **using Pacman:**

```console
sudo pacman -S nftables
```

## `nft` Command Syntax

```plaintext
nft [ -nNscaeSupyjtT ] [ -I directory ] [ -f filename | -i | cmd ... ]
```

*   `-n`: Show rule numbers in output.
    
*   `-N`: Print more detailed output for rules.
    
*   `-s`: Show statistics for rules.
    
*   `-c`: Show chain name for each rule.
    
*   `-a`: Print the rule with its address family and protocol.
    
*   `-e`: Expand rule details.
    
*   `-S`: Print the ruleset (configuration).
    
*   `-u`: Enable update mode for dynamic changes.
    
*   `-p`: Print the current ruleset in a parsable format.
    
*   `-y`: Show detailed object types.
    
*   `-j`: Output in JSON format.
    
*   `-t`: Show output in table format.
    
*   `-T`: Show tokenized output.
    
*   `-I directory`: Use the specified directory for configuration files.
    
*   `-f filename`: Load rules from a file.
    
*   `-i`: Interactive mode to modify rules directly.
    
*   `cmd ...`: Command arguments to manipulate the nftables rules.
    

## Manage the `nftables` System Service

The `nftables` service manages your firewall rules and ensures persistence across system reboots.

**1\. Enable the** `nftables` **service to start at boot:**

```console
sudo systemctl enable nftables
```

**2\. Start the** `nftables` **service:**

```console
sudo systemctl start nftables
```

**3\. Verify the service status:**

```console
sudo systemctl status nftables
```

Your output should be similar to the one below:

```plaintext
● nftables.service - Netfilter Tables
     Loaded: loaded (/usr/lib/systemd/system/nftables.service; enabled; preset: disabled)
     Active: active (exited) since Mon 2025-03-31 17:52:35 UTC; 5s ago
       Docs: man:nft(8)
    Process: 1838 ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf (code=exited, status=0/SUCCESS)
   Main PID: 1838 (code=exited, status=0/SUCCESS)
        CPU: 11ms

Mar 31 17:52:35 nftables-03 systemd[1]: Starting Netfilter Tables...
Mar 31 17:52:35 nftables-03 systemd[1]: Finished Netfilter Tables.
```

## Tables, Chains, and Policies in `nftables`

In `nftables`, rules are organized into **tables**, which contain **chains** that define how traffic is processed. Each chain is associated with a **hook** (`input`, `output`, `forward`) that determines when it is evaluated, and chains have **policies** that define the default action when no explicit rule matches a packet.

`nftables` supports multiple address families for handling different network protocols:

*   `inet`: Unified handling for both IPv4 and IPv6.
    
*   `ip`: IPv4-only filtering.
    
*   `ip6`: IPv6-only filtering.
    
*   `arp`: ARP packet filtering.
    
*   `bridge`: Ethernet bridge packet filtering.
    
*   `netdev`: Packet filtering at the network device level.
    

Each chain is linked to a specific traffic type: `input` controls packets destined for the local machine, `output` manages packets leaving the machine, and `forward` handles packets routed through the machine.

The default policy determines whether unmatched traffic is accepted, dropped, or rejected:

*   `policy accept` - Allows all traffic by default (useful for testing or initial configurations).
    
*   `policy drop` - Discards unmatched packets (most secure, commonly used in production environments).
    
*   `policy reject` - Sends a rejection notice to the source (informative but reveals the presence of the server, which might not be ideal for security).
    

> **Warning:** Using `accept` as the default policy is not recommended for production environments, as it allows all traffic unless explicitly blocked.

## Manage the Default `nftables` Configuration

**1\. View the list of existing** `nftables` **rules:**

```console
sudo nft list tables
```

Your output should be similar to the one below:

```plaintext
table ip filter
table ip6 filter
```

**2\. View the default** `nftables` **configuration file:**

```console
cat /etc/nftables.conf
```

Your output should be similar to the one below:

```ini
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy accept;
    }
    chain forward {
        type filter hook forward priority 0; policy accept;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}
```

## Configure Basic `nftables` Firewall Rules

### Create Initial Table and Chains

**1\. Create a new firewall table for IPv4 and IPv6 traffic:**

```console
sudo nft add table inet my_table
```

This creates a new table named `my_table` that can process both IPv4 and IPv6 packets using the `inet` family. Replace `my_table` with your preferred table name; it's used throughout the rest of this guide.

**2\. Add an input chain with a default accept policy:**

```console
sudo nft add chain inet my_table input { type filter hook input priority 0 \; }
```

This creates a filtering chain that applies to incoming traffic. `type filter` designates it as a filtering chain, `hook input` ensures it processes incoming packets, and `priority 0` determines the execution order (lower values run first).

**3\. Create a forward chain with a default accept policy:**

```console
sudo nft add chain inet my_table forward { type filter hook forward priority 0\; }
```

This creates a filtering chain that applies to forwarded traffic, using `hook forward` to process forwarded packets.

**4\. Allow established connections to keep existing communications:**

```console
sudo nft add rule inet my_table input ct state established,related accept
```

This rule uses connection tracking: `ct state` enables connection tracking, and `established` allows packets from existing connections.

### Add, List, and Delete Rules

**1\. Add a rule to block incoming traffic from a specific IP address:**

```console
sudo nft add rule inet my_table input ip saddr 192.0.2.10 drop
```

This adds a rule to the `my_table` table's `input` chain, blocking incoming traffic from the IP address `192.0.2.10`.

**2\. View all active** `nftables` **rules to verify configurations:**

```console
sudo nft list ruleset
```

**3\. List the rules with handle numbers:**

```console
sudo nft list chain inet my_table input
```

Your output should be similar to the one below:

```ini
table inet my_table {
        chain input {
                type filter hook input priority filter; policy accept;
                ct state established,related accept
                ip saddr 192.0.2.10 drop
        }
}
```

**4\. Identify the handle number from the output and delete the corresponding rule:**

```console
sudo nft delete rule inet my_table input handle 0
```

This deletes the rule with handle number `0` from the `input` chain of the `my_table` table.

### Implement Essential Security Rules

**1\. Allow SSH access to your server:**

```console
sudo nft add rule inet my_table input tcp dport 22 accept
```

This matches TCP packets targeting destination port `22` (the default SSH port) and accepts them.

**2\. Block a specific IP address that shows suspicious activity:**

```console
sudo nft add rule inet my_table input ip saddr 192.0.2.10 drop
```

This matches packets from the specified source IP address and drops them without notifying the sender.

**3\. Allow HTTP traffic on port 80:**

```console
sudo nft add rule inet my_table input tcp dport 80 accept
```

**4\. Allow HTTPS traffic on port 443:**

```console
sudo nft add rule inet my_table input tcp dport 443 accept
```

**5\. Drop all other incoming traffic:**

```console
sudo nft add rule inet my_table input ip daddr 0.0.0.0/0 counter drop
```

This drops all incoming traffic by default while allowing only the preconfigured rules (SSH, HTTP, HTTPS), effectively blocking unwanted access.

### Enable NAT and Port Forwarding

**1\. Enable NAT functionality in the Linux kernel by editing** `/etc/sysctl.conf`**:**

```console
sudo vim /etc/sysctl.conf
```

**2\. Uncomment the following line:**

```ini
...
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
....
```

Save and close the file.

**3\. Apply the changes:**

```console
sudo sysctl -p
```

**4\. Create a new NAT table to handle network address translation for your server:**

```console
sudo nft add table nat
```

**5\. Create a postrouting chain in the NAT table to process packets after routing:**

```console
sudo nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
```

**6\. Add a masquerading rule for your internal network to enable internet connection sharing:**

```console
sudo nft add rule nat postrouting ip saddr 192.0.2.0/24 oif enp1s0 masquerade
```

This tells `nftables` to perform NAT (masquerading) for outgoing traffic from the `192.0.2.0/24` network. The `oif enp1s0` part specifies that the rule applies to traffic leaving the `enp1s0` interface, typically the interface connected to the internet.

### Save Rules Persistently Across Reboots

**1\. Save your current firewall configuration to the** `/etc/nftables.conf` **file:**

```console
sudo nft list ruleset | sudo tee  /etc/nftables.conf
```

**2\. Apply saved rules from the configuration file:**

```console
sudo nft -f /etc/nftables.conf
```

**3\. Restart the** `nftables` **service:**

```console
sudo systemctl restart nftables
```

This reloads the firewall rules and ensures they persist after reboots.

## Advanced Rule Configuration

**1\. Implement rate limiting for SSH connections:**

```console
sudo nft add rule inet my_table input tcp dport 22 limit rate 3/minute accept
```

This targets SSH traffic (port 22), limits connections to 3 per minute, and accepts connections within the limit to help prevent brute force attacks.

**2\. Create the prerouting chain in the nat table (if it doesn't exist):**

```console
sudo nft add chain ip nat prerouting { type nat hook prerouting priority 0 \; }
```

**3\. Add the port forwarding rule:**

```console
sudo nft add rule nat prerouting tcp dport 80 dnat to 192.0.2.10
```

This captures incoming HTTP traffic (port 80) and forwards it to an internal server (`192.0.2.10`), enabling you to host internal web services.

**4\. Enable logging for dropped packets:**

```console
sudo nft 'add rule inet my_table input log prefix "nftables-drop: " drop'
```

This logs packets before dropping them, with a custom prefix for easy filtering, and stores the logs in the system journal.

**5\. Flush all the** `nftables` **rules:**

```console
sudo nft flush ruleset
```

> **Warning:** Be cautious when flushing the ruleset, as this removes all existing `nftables` rules, effectively resetting your firewall configuration.

## Next Steps

*   Combine `nftables` sets and maps to manage large groups of IP addresses or ports more efficiently.
    
*   Script your `nftables` ruleset into a version-controlled configuration file for repeatable deployments.
    
*   Pair `nftables` logging with a centralized log aggregator to monitor dropped and rejected traffic.
    
*   Explore IPv6-specific rules using the `ip6` address family for dual-stack environments.
    

For the full guide with additional tips, visit the original article on [**Vultr Docs**](https://docs.vultr.com/how-to-install-and-use-nftables-in-linux).
