# nftables — The Modern Linux Firewall with nft

> Practical guide to nftables — the modern iptables successor. Tables, chains, rules, sets and NAT with the nft syntax, plus hardened firewall setups.

Source: https://www.jpkc.com/db/en/cheatsheets/security/nftables/

<!-- PROSE:intro -->
nftables is the unified firewall of the Linux kernel, replacing the legacy tools iptables, ip6tables, arptables, and ebtables under a single command: `nft`. Instead of four separate toolsets, you manage IPv4, IPv6, ARP, and bridge traffic with one consistent syntax of tables, chains, rules, sets, and maps. Rule changes are applied atomically, and named sets keep large address lists fast and maintainable. This guide takes you from listing existing rules through match expressions and NAT to a complete, hardened firewall setup.
<!-- PROSE:intro:end -->

## Listing Rules

`nft list ruleset` — List all rules across all tables and families.

```bash
sudo nft list ruleset
```

`nft list tables` — List all tables.

```bash
sudo nft list tables
```

`nft list table FAMILY TABLE` — List all chains and rules in a specific table.

```bash
sudo nft list table inet filter
```

`nft list chain FAMILY TABLE CHAIN` — List rules in a specific chain.

```bash
sudo nft list chain inet filter input
```

`nft -a list ruleset` — List all rules with handle numbers (needed for deleting).

```bash
sudo nft -a list ruleset
```

`nft -s list ruleset` — List rules without string translation (numeric protocols/ports).

```bash
sudo nft -s list ruleset
```

`nft -j list ruleset` — List rules in JSON format.

```bash
sudo nft -j list ruleset
```

## Address Families

`ip` — IPv4 only (equivalent to iptables).

`ip6` — IPv6 only (equivalent to ip6tables).

`inet` — Both IPv4 and IPv6 in one table. Recommended for most use cases.

`arp` — ARP protocol (equivalent to arptables).

`bridge` — Bridge/layer 2 filtering (equivalent to ebtables).

`netdev` — Ingress filtering at the device level (before routing).

## Table Management

`nft add table FAMILY NAME` — Create a new table.

```bash
sudo nft add table inet filter
```

`nft delete table FAMILY NAME` — Delete a table and all its chains/rules.

```bash
sudo nft delete table inet filter
```

`nft flush table FAMILY NAME` — Remove all rules from a table (keep chains).

```bash
sudo nft flush table inet filter
```

`nft flush ruleset` — Remove all tables, chains, and rules. **Caution:** with remote access and a default `policy drop`, this locks you out — allow SSH/established first and test locally only.

```bash
sudo nft flush ruleset
```

## Chain Management

`nft add chain FAMILY TABLE CHAIN '{ type TYPE hook HOOK priority PRIO; policy POLICY; }'` — Create a base chain with hook, priority, and policy. Be careful with `policy drop` before adding accept rules (e.g. for SSH/established) – see the hardened setup below.

```bash
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
```

`nft add chain FAMILY TABLE CHAIN` — Create a regular (non-base) chain for organizing rules.

```bash
sudo nft add chain inet filter tcp_chain
```

`nft delete chain FAMILY TABLE CHAIN` — Delete a chain (must be empty).

```bash
sudo nft delete chain inet filter tcp_chain
```

`nft flush chain FAMILY TABLE CHAIN` — Remove all rules from a chain.

```bash
sudo nft flush chain inet filter input
```

## Adding Rules

`nft add rule FAMILY TABLE CHAIN STATEMENT` — Append a rule to the end of a chain.

```bash
sudo nft add rule inet filter input tcp dport 80 accept
```

`nft insert rule FAMILY TABLE CHAIN STATEMENT` — Insert a rule at the beginning of a chain.

```bash
sudo nft insert rule inet filter input tcp dport 443 accept
```

`nft add rule FAMILY TABLE CHAIN position HANDLE STATEMENT` — Add a rule after a specific handle.

```bash
sudo nft add rule inet filter input position 8 tcp dport 8080 accept
```

`nft insert rule FAMILY TABLE CHAIN position HANDLE STATEMENT` — Insert a rule before a specific handle.

```bash
sudo nft insert rule inet filter input position 8 tcp dport 22 accept
```

## Deleting Rules

`nft delete rule FAMILY TABLE CHAIN handle HANDLE` — Delete a rule by handle number (use -a to find handles).

```bash
sudo nft delete rule inet filter input handle 12
```

`nft flush chain FAMILY TABLE CHAIN` — Delete all rules in a chain.

```bash
sudo nft flush chain inet filter input
```

## Match Expressions

`tcp dport PORT` — Match TCP destination port.

```bash
sudo nft add rule inet filter input tcp dport 22 accept
```

`udp dport PORT` — Match UDP destination port.

```bash
sudo nft add rule inet filter input udp dport 53 accept
```

`tcp dport { P1, P2, P3 }` — Match multiple ports using anonymous sets.

```bash
sudo nft add rule inet filter input tcp dport { 80, 443, 8080 } accept
```

`tcp dport P1-P2` — Match a port range.

```bash
sudo nft add rule inet filter input tcp dport 8000-9000 accept
```

`ip saddr ADDRESS` — Match source IPv4 address.

```bash
sudo nft add rule inet filter input ip saddr 192.168.1.0/24 accept
```

`ip daddr ADDRESS` — Match destination IPv4 address.

```bash
sudo nft add rule inet filter output ip daddr 10.0.0.0/8 drop
```

`ip6 saddr ADDRESS` — Match source IPv6 address.

```bash
sudo nft add rule inet filter input ip6 saddr fd00::/8 accept
```

`ip saddr { A1, A2, A3 }` — Match multiple source addresses.

```bash
sudo nft add rule inet filter input ip saddr { 10.0.0.1, 10.0.0.2 } accept
```

`iifname INTERFACE` — Match incoming interface name.

```bash
sudo nft add rule inet filter input iifname "eth0" accept
```

`oifname INTERFACE` — Match outgoing interface name.

```bash
sudo nft add rule inet filter output oifname "lo" accept
```

`meta l4proto icmp` — Match ICMP protocol.

```bash
sudo nft add rule inet filter input meta l4proto icmp accept
```

`ct state { established, related }` — Match connection tracking state.

```bash
sudo nft add rule inet filter input ct state { established, related } accept
```

`ct state invalid` — Match invalid connection state.

```bash
sudo nft add rule inet filter input ct state invalid drop
```

`ct state new` — Match new connections.

```bash
sudo nft add rule inet filter input ct state new tcp dport 22 accept
```

## Actions (Verdicts)

`accept` — Allow the packet.

`drop` — Silently discard the packet.

`reject` — Reject the packet with an ICMP error.

`reject with TYPE` — Reject with a specific message type.

```bash
sudo nft add rule inet filter input tcp dport 23 reject with tcp reset
```

`log prefix "TEXT"` — Log the packet with a prefix.

```bash
sudo nft add rule inet filter input tcp dport 22 log prefix "SSH: " accept
```

`counter` — Count packets and bytes matching this rule.

```bash
sudo nft add rule inet filter input tcp dport 80 counter accept
```

`jump CHAIN` — Jump to another chain for further processing.

```bash
sudo nft add rule inet filter input tcp dport { 80, 443 } jump web_chain
```

`return` — Return from the current chain.

`limit rate N/TIME` — Rate limit matches (e.g., 5/minute, 1/second).

```bash
sudo nft add rule inet filter input icmp type echo-request limit rate 1/second accept
```

## NAT

`nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; }'` — Create a NAT postrouting chain.

```bash
sudo nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; }'
```

`masquerade` — Masquerade outgoing traffic (dynamic SNAT).

```bash
sudo nft add rule ip nat postrouting oifname "eth0" masquerade
```

`dnat to ADDRESS:PORT` — Destination NAT (port forwarding).

```bash
sudo nft add rule ip nat prerouting tcp dport 8080 dnat to 192.168.1.100:80
```

`snat to ADDRESS` — Source NAT with a static IP.

```bash
sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/8 snat to 203.0.113.1
```

`redirect to :PORT` — Redirect to a local port.

```bash
sudo nft add rule ip nat prerouting tcp dport 80 redirect to :8080
```

## Named Sets

`nft add set FAMILY TABLE NAME '{ type TYPE; }'` — Create a named set.

```bash
sudo nft add set inet filter blocked_ips '{ type ipv4_addr; }'
```

`nft add element FAMILY TABLE SET '{ ELEM1, ELEM2 }'` — Add elements to a set.

```bash
sudo nft add element inet filter blocked_ips '{ 10.0.0.1, 10.0.0.2 }'
```

`nft delete element FAMILY TABLE SET '{ ELEM }'` — Remove an element from a set.

```bash
sudo nft delete element inet filter blocked_ips '{ 10.0.0.1 }'
```

`nft list set FAMILY TABLE SET` — Show contents of a named set.

```bash
sudo nft list set inet filter blocked_ips
```

`ip saddr @SET_NAME drop` — Use a named set in a rule.

```bash
sudo nft add rule inet filter input ip saddr @blocked_ips drop
```

## Save & Restore

`nft list ruleset > FILE` — Save the current ruleset to a file. Without `/etc/nftables.conf` plus the enabled `nftables` service, rules are not persistent and are lost on reboot.

```bash
sudo nft list ruleset > /etc/nftables.conf
```

`nft -f FILE` — Load rules from a file.

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

`nft -c -f FILE` — Check/validate a ruleset file without applying it.

```bash
sudo nft -c -f /etc/nftables.conf
```

## Common Firewall Setup

`nft add table inet filter` — Step 1: Create a filter table for IPv4 and IPv6.

```bash
sudo nft add table inet filter
```

`nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'` — Step 2: Create input chain with drop policy. **Caution:** from now on everything without a matching rule is dropped — add the SSH/established rules (steps 4 and 6) first, or you will lock yourself out over remote access.

```bash
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
```

`nft add rule inet filter input iifname "lo" accept` — Step 3: Allow loopback traffic.

```bash
sudo nft add rule inet filter input iifname "lo" accept
```

`nft add rule inet filter input ct state { established, related } accept` — Step 4: Allow established connections.

```bash
sudo nft add rule inet filter input ct state { established, related } accept
```

`nft add rule inet filter input ct state invalid drop` — Step 5: Drop invalid packets.

```bash
sudo nft add rule inet filter input ct state invalid drop
```

`nft add rule inet filter input tcp dport { 22, 80, 443 } accept` — Step 6: Allow SSH, HTTP, HTTPS.

```bash
sudo nft add rule inet filter input tcp dport { 22, 80, 443 } accept
```

`nft add rule inet filter input icmp type echo-request accept` — Step 7: Allow ping.

```bash
sudo nft add rule inet filter input icmp type echo-request accept
```

<!-- PROSE:outro -->
## Conclusion

With nftables you manage IPv4, IPv6, and bridge filtering in a single, consistent framework instead of four separate legacy tools. Draft new rulesets in an `/etc/nftables.conf`, validate them with `nft -c -f`, and enable the `nftables` service so they survive a reboot. And always test a default `policy drop` locally first: on a remote server, a forgotten SSH accept quickly costs you access.

## Further Reading

- [nftables wiki](https://wiki.nftables.org/) – official documentation and how-tos
- [nft(8) – manual page](https://man7.org/linux/man-pages/man8/nft.8.html) – every option at a glance
- [nftables – Wikipedia](https://en.wikipedia.org/wiki/Nftables) – background and history
<!-- PROSE:outro:end -->

## Related Commands

- [age](https://www.jpkc.com/db/en/cheatsheets/security/age/) – modern file encryption on the command line
- [clamav](https://www.jpkc.com/db/en/cheatsheets/security/clamav/) – open-source virus scanner for Linux servers
- [fail2ban](https://www.jpkc.com/db/en/cheatsheets/security/fail2ban/) – bans attacker IPs based on log patterns

