> For the complete documentation index, see [llms.txt](https://docs.glesys.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.glesys.com/products/compute/guides-for-server-management/set-up-a-firewall-on-debian-11-or-newer-using-nftables.md).

# Set up a firewall on Debian 11 or newer using nftables

***

Having a functional firewall on the server is an important step in security work. In this guide, we will create a basic firewall that you can later extend with your own rules. The guide assumes a newly installed Cloud VM running Debian 11. If you want to set up a firewall on Ubuntu 22.04, we have a guide for that here: "[Set up a firewall on Ubuntu 22.04 or newer using UFW](/products/compute/guides-for-server-management/set-up-a-firewall-on-ubuntu-22.04-or-newer-using-ufw.md)".

In Debian 11 and newer, `iptables` has been replaced by `nftables`. There are several advantages to `nftables`, including the ability to create rules that apply to both IPv4 and IPv6. The syntax in `nftables` is also somewhat simplified. Another benefit is that there is a ready‑made file where we can place the rules, and a ready‑made `systemd` unit to start and stop the firewall.

## Activate the firewall

All the required programs are already installed on Debian 11. However, you still need to enable and start the firewall. Do this with the following two commands:

{% code title="Multiple commands" %}

```
sudo systemctl enable nftables.service
sudo systemctl start nftables.service
```

{% endcode %}

Now you can test that the firewall is working by listing the rules. By default, nothing is filtered, but Debian provides a basic configuration for *input*, *forward*, and *output*. List the rules with the command:

{% code title="Command" %}

```
sudo nft list ruleset
```

{% endcode %}

The output should look similar to this:

{% code title="Output" %}

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

        chain forward {
                type filter hook forward priority filter; policy accept;
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}
```

{% endcode %}

The basic configuration we see above comes from the file `/etc/nftables.conf`, confirming that `nftables` is working. If you display the contents of that file, it will match the output from the command you just ran. You can try this with `cat /etc/nftables.conf`. The output should be:

{% code title="/etc/nftables.conf" %}

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

flush ruleset

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

{% endcode %}

## Create firewall rules

Now it’s time to add some firewall rules. The rules we’ll create here, in this example, allow incoming traffic for SSH, HTTP, and HTTPS. We'll also permit ICMP ("ping") and traceroute. To make IPv6 work, we'll allow both ICMP and the "next header". For the local loopback interface (`lo`), we'll allow all traffic. To ensure that the traffic we initiate ourselves—from the server—works correctly, we'll also allow incoming traffic that is related to or part of an already established connection.

For outbound traffic from the server, we'll also allow HTTP, HTTPS, ICMP ("ping"), and traceroute. To enable name resolution, we'll allow DNS queries on UDP port 53. We'll also allow NTP queries on UDP port 123 for time synchronization.

The final rule for both inbound and outbound traffic is a rule that drops (`drop`) all traffic. Rules are evaluated in the order they appear, so the rules that allow traffic will be processed before the final rule that blocks everything.

All of these rules apply to both IPv4 and IPv6—except for the special rules for ICMPv6 and "next header," which apply only to IPv6.

Add the rules to the file `/etc/nftables.conf`. Open the file with `nano` or `vi` and edit it so that it looks like the example below (the file is owned by root, so you need to open it with `sudo nano /etc/nftables.conf` or `sudo vi /etc/nftables.conf`).

{% code title="/etc/nftables.conf" %}

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

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;
                # accept all traffic on the lo interface (localhost)
                iif lo accept

                # accept incoming traffic that was initiated by us
                ct state established,related accept

                # accept incoming connections to SSH, HTTP, and HTTPS
                tcp dport { 22, 80, 443 } ct state new counter accept

                # allow incoming traceroute
                udp dport 33434-33524 ct state new counter accept

                # allow "next header" and ping over IPv6, otherwise we lose IPv6 connectivity
                ip6 nexthdr icmpv6 counter accept

                # allow incoming icmp (ping) over IPv4
                ip protocol icmp counter accept

                # block (and count) all other incoming traffic
                counter drop
        }
        chain forward {
                type filter hook forward priority 0;
        }
        chain output {
                type filter hook output priority 0;
                # accept all traffic on the lo interface (localhost)
                iif lo accept

                # acceptera all utgående trafik som redan har initierats
                ct state established,related accept

                # accept all outgoing traffic to HTTP and HTTPS
                tcp dport { 80, 443 } ct state new counter accept

                # accept outgoing requests to DNS servers and NTP servers
                udp dport { 53, 123 } ct state new counter accept

                # allow traceroute
                udp dport 33434-33524 ct state new counter accept

                # allow "next header" and ping over IPv6, otherwise we lose IPv6 connectivity
                ip6 nexthdr icmpv6 counter accept

                # allow icmp (ping) over IPv4
                ip protocol icmp counter accept

                # block (and count) all other outgoing traffic
                counter drop
        }
}
```

{% endcode %}

After you have added all the rules and saved the file, reload the firewall rules for them to take effect. Do this with the `systemctl` command:

{% code title="Command" %}

```
sudo systemctl reload nftables.service
```

{% endcode %}

Now, let's verify that it works with `sudo nft list ruleset`. The output should be:

{% code title="Output" %}

```
table inet filter {
        chain input {
                type filter hook input priority filter; policy accept;
                iif "lo" accept
                ct state established,related accept
                tcp dport { 22, 80, 443 } ct state new counter packets 2 bytes 84 accept
                udp dport 33434-33524 ct state new counter packets 0 bytes 0 accept
                ip6 nexthdr ipv6-icmp counter packets 0 bytes 0 accept
                ip protocol icmp counter packets 0 bytes 0 accept
                counter packets 17 bytes 1321 drop
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
        }

        chain output {
                type filter hook output priority filter; policy accept;
                iif "lo" accept
                ct state established,related accept
                tcp dport { 80, 443 } ct state new counter packets 0 bytes 0 accept
                udp dport { 53, 123 } ct state new counter packets 0 bytes 0 accept
                udp dport 33434-33524 ct state new counter packets 0 bytes 0 accept
                ip6 nexthdr ipv6-icmp counter packets 0 bytes 0 accept
                ip protocol icmp counter packets 0 bytes 0 accept
                counter packets 0 bytes 0 drop
        }
}
```

{% endcode %}

In some of the lines of the output, you’ll see `counter packets 2 bytes 84`. This counter tracks how many packets have matched that rule and their total size. If you don’t need to count packets, remove the keyword `counter` from the rules in `/etc/nftables.conf`. If you don’t want to open `nftables.conf` in a text editor and edit it manually, you can do it automatically with the following `sed` command:

{% code title="Command" %}

```
sudo sed -i 's/counter//g' /etc/nftables.conf
```

{% endcode %}

If you make changes to the `/etc/nftables.conf` file, you must reload the firewall rules again with `sudo systemctl reload nftables.service` for the changes to take effect.

## Make temporary changes

Changes you make in `/etc/nftables.conf` are permanent and are loaded automatically when the server reboots. However, sometimes you might need to create temporary rules—for example, during testing or when experimenting with new services. In those cases, you can use the `nft add rule` command. These rules are temporary; they disappear after a reboot.

New rules added with `nft add rule` are placed at the end of the list, i.e., after the rule that blocks all traffic. Therefore, you need to be able to insert rules at specific positions in the list. Start by listing all the rules again, this time adding the `-a` flag.

{% code title="Command" %}

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

{% endcode %}

The output now includes numbered *handles* that you can use:

{% code title="Output" %}

```
table inet filter { # handle 13
        chain input { # handle 1
                type filter hook input priority filter; policy accept;
                iif "lo" accept # handle 7
                ct state established,related accept # handle 8
                tcp dport { 22, 80, 443 } ct state new counter packets 0 bytes 0 accept # handle 9
                udp dport 33434-33524 ct state new counter packets 0 bytes 0 accept # handle 10
                ip6 nexthdr ipv6-icmp counter packets 0 bytes 0 accept # handle 11
                ip protocol icmp counter packets 0 bytes 0 accept # handle 12
                counter packets 0 bytes 0 drop # handle 13
        }

        chain forward { # handle 2
                type filter hook forward priority filter; policy accept;
        }

        chain output { # handle 3
                type filter hook output priority filter; policy accept;
                iif "lo" accept # handle 14
                ct state established,related accept # handle 15
                tcp dport { 80, 443 } ct state new counter packets 0 bytes 0 accept # handle 16
                udp dport { 53, 123 } ct state new counter packets 0 bytes 0 accept # handle 17
                udp dport 33434-33524 ct state new counter packets 0 bytes 0 accept # handle 18
                ip6 nexthdr ipv6-icmp counter packets 0 bytes 0 accept # handle 19
                ip protocol icmp counter packets 0 bytes 0 accept # handle 20
                counter packets 0 bytes 0 drop # handle 21
        }
}
```

{% endcode %}

In this example, we'll add a new rule to allow incoming traffic on TCP port 8080 (http‑alt), then remove it again. Place the rule after the `traceroute` rule—that is, after *handle 10*. Do this with:

{% code title="Command" %}

```
sudo nft add rule inet filter input handle 10 tcp dport 8080 accept
```

{% endcode %}

Let's list the rules again, but this time limit the list to the *input* rules. The new rule should be placed after *handle 10*:

{% code title="Command" %}

```
sudo nft -a list chain inet filter input
```

{% endcode %}

{% code title="Output" %}

```
table inet filter {
        chain input { # handle 1
                type filter hook input priority filter; policy accept;
                iif "lo" accept # handle 7
                ct state established,related accept # handle 8
                tcp dport { 22, 80, 443 } ct state new counter packets 12 bytes 568 accept # handle 9
                udp dport 33434-33524 ct state new counter packets 0 bytes 0 accept # handle 10
                tcp dport 8080 accept # handle 22
                ip6 nexthdr ipv6-icmp counter packets 15 bytes 1080 accept # handle 11
                ip protocol icmp counter packets 0 bytes 0 accept # handle 12
                counter packets 184 bytes 21043 drop # handle 13
        }
}
```

{% endcode %}

To delete the rule again, use its handle, 22:

```
sudo nft delete rule inet filter input handle 22
```

## If you lock yourself out by mistake

Be careful when loading new firewall rules so you don’t lock yourself out of the SSH service (TCP port 22). If you do accidentally lock yourself out, you can log in through the console in Glesys Cloud. You’ll find it under **Computer** → **Virtual machines**. Click **Actions** next to your server’s name and select **Console**. Once you’re logged in, you can edit the `/etc/nftables.conf` file and fix the issue.

For more information on logging in to the console and booting the server in single-user mode, see [Connect to the VM console](/products/compute/kvm-virtual-machines/how-tos/connect-to-the-vm-console.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.glesys.com/products/compute/guides-for-server-management/set-up-a-firewall-on-debian-11-or-newer-using-nftables.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
