$linuxjunkies
>

Turn a Linux Box into a Router

Configure a Linux machine as a full router: static interfaces, kernel IP forwarding, nftables NAT, dnsmasq DHCP, and DNS forwarding — step by step.

AdvancedUbuntuDebianFedoraArch12 min readUpdated June 7, 2026

Before you start

  • Two or more network interfaces (physical or virtual) installed and visible to the OS
  • Root or sudo access on the machine
  • The machine connected to an upstream modem or ISP device on the WAN interface
  • Basic familiarity with Linux networking concepts (IP addresses, subnets, gateways)

Turning a spare Linux machine into a router gives you full control over your network: custom firewall rules, DNS filtering, traffic shaping, and more — without proprietary firmware. This guide walks through every layer: interface setup, kernel IP forwarding, NAT with nftables, a DHCP server, and a caching DNS forwarder. You need at least two network interfaces (the WAN-facing NIC and one or more LAN-facing NICs).

1. Identify and Name Your Interfaces

Modern kernels use predictable interface names. Confirm what you have:

ip link show

Decide which interface is WAN (upstream, e.g. from your ISP modem) and which is LAN (downstream, to your switch or devices). Throughout this guide WAN=enp1s0 and LAN=enp2s0 are used as examples — substitute your real names.

2. Configure Network Interfaces

Give the LAN interface a static IP. The WAN interface usually gets its address from your ISP via DHCP. Configure with systemd-networkd, which is available on every major distro.

Enable systemd-networkd

sudo systemctl enable --now systemd-networkd

WAN interface (DHCP from ISP)

sudo tee /etc/systemd/network/10-wan.network <<'EOF'
[Match]
Name=enp1s0

[Network]
DHCP=yes
DNS=127.0.0.1
EMitLLDP=no
IPForward=yes
EOF

LAN interface (static)

sudo tee /etc/systemd/network/20-lan.network <<'EOF'
[Match]
Name=enp2s0

[Network]
Address=192.168.10.1/24
IPForward=yes
ConfigureWithoutCarrier=yes
EOF
sudo networkctl reload

Verify both interfaces are configured:

networkctl status enp1s0 enp2s0

3. Enable Kernel IP Forwarding

By default the kernel drops packets that arrive on one interface but are destined for another. You must enable forwarding persistently.

sudo tee /etc/sysctl.d/99-router.conf <<'EOF'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sudo sysctl --system

Confirm it took effect (should print 1):

sysctl net.ipv4.ip_forward

4. Set Up NAT and the Firewall with nftables

nftables is the modern replacement for iptables/ip6tables/arptables and is the default on Debian 11+, Fedora 32+, and RHEL 9+. The ruleset below implements masquerade NAT on the WAN interface and a stateful firewall that forwards established traffic, blocks unsolicited inbound on WAN, and allows everything from LAN.

Install nftables

# Debian/Ubuntu
sudo apt install nftables

# Fedora/RHEL/Rocky
sudo dnf install nftables

# Arch
sudo pacman -S nftables

Write the ruleset

sudo tee /etc/nftables.conf <<'EOF'
#!/usr/sbin/nft -f
flush ruleset

define WAN = enp1s0
define LAN = enp2s0
define LAN_NET = 192.168.10.0/24

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        iifname lo accept
        ct state established,related accept

        # Allow ICMP
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # Allow all from LAN
        iifname $LAN accept

        # Allow SSH on WAN (remove or restrict after setup)
        iifname $WAN tcp dport 22 accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        ct state established,related accept
        iifname $LAN oifname $WAN accept
    }

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

table ip nat {
    chain postrouting {
        type nat hook postrouting priority srcnat;
        oifname $WAN masquerade
    }
}
EOF
sudo systemctl enable --now nftables
sudo nft -f /etc/nftables.conf

Check the loaded ruleset:

sudo nft list ruleset

5. DHCP Server with dnsmasq

dnsmasq handles both DHCP and DNS forwarding in a single lightweight daemon — ideal for a home or small-office router. Install it first.

# Debian/Ubuntu
sudo apt install dnsmasq

# Fedora/RHEL/Rocky
sudo dnf install dnsmasq

# Arch
sudo pacman -S dnsmasq

Configure dnsmasq

Back up the default config, then write a clean one:

sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak

sudo tee /etc/dnsmasq.conf <<'EOF'
# Listen only on the LAN interface
interface=enp2s0
bind-interfaces

# DHCP pool: hand out .100-.200, 24h lease
dhcp-range=192.168.10.100,192.168.10.200,24h
dhcp-option=option:router,192.168.10.1
dhcp-option=option:dns-server,192.168.10.1

# Optional: reserve an address by MAC
# dhcp-host=aa:bb:cc:dd:ee:ff,192.168.10.50,myserver

# DNS: forward upstream queries
# Use your ISP's DNS or a public resolver
server=1.1.1.1
server=9.9.9.9

# Cache 512 entries
cache-size=512

# Don't read /etc/resolv.conf for upstream servers
no-resolv

# Local domain
domain=lan
local=/lan/
expand-hosts
EOF
sudo systemctl enable --now dnsmasq

If dnsmasq conflicts with systemd-resolved (common on Ubuntu), disable resolved first:

sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo 'nameserver 127.0.0.1' | sudo tee /etc/resolv.conf

6. Verify End-to-End Connectivity

Connect a client to the LAN side (directly or via switch). The client should receive a DHCP lease in the 192.168.10.100–200 range with gateway and DNS pointing to 192.168.10.1.

On the router

# Confirm DHCP leases
cat /var/lib/misc/dnsmasq.leases

# Check NAT translation is happening
sudo nft list table ip nat

# Watch forwarded packets in real time
sudo nft monitor trace   # Ctrl-C to stop

On the client

ip route show
ping -c3 192.168.10.1
ping -c3 1.1.1.1
dig @192.168.10.1 example.com

7. Persist Everything Across Reboots

All three services must start in the right order. systemd handles this automatically because dnsmasq has After=network-online.target in its unit, but double-check:

sudo systemctl is-enabled systemd-networkd nftables dnsmasq

All three should report enabled. Reboot and re-run the client-side verification above.

Troubleshooting

  • Client gets no DHCP lease: Check sudo journalctl -u dnsmasq -n 50. Confirm dnsmasq is bound to the correct interface and that nftables is not blocking UDP 67/68 on the LAN input chain (the ruleset above allows all LAN traffic, so this should be fine).
  • Client can ping 192.168.10.1 but not the internet: IP forwarding may not be active (sysctl net.ipv4.ip_forward must return 1). Also verify the forward chain in nftables allows LAN→WAN and that the nat postrouting masquerade rule is loaded.
  • nft: command not found after package install: On RHEL/Rocky 8, the package is nftables but the binary path may need a shell reload: run hash -r or open a new terminal.
  • WAN interface not getting a DHCP address: Some ISPs lock by MAC. If you replaced an existing router, clone its MAC: ip link set enp1s0 address AA:BB:CC:DD:EE:FF and add a permanent MACAddress= line to your 10-wan.network file.
  • dnsmasq fails to start — address already in use: Another process (often systemd-resolved or NetworkManager's internal dnsmasq) is holding port 53. Check with sudo ss -tulpn | grep ':53' and disable the conflicting service.
tested on:Ubuntu 24.04Debian 12Fedora 40Arch 2024.05

Frequently asked questions

Can I use this setup with a USB-to-Ethernet adapter as a second NIC?
Yes, USB NICs work, but they have higher latency and CPU overhead and can be unreliable under sustained load. For a production router use a PCIe NIC. If you do use USB, pin the interface name with a udev rule so it doesn't change between reboots.
Does this guide support IPv6 routing?
Kernel forwarding for IPv6 is enabled in step 3, and the nftables ruleset accepts ICMPv6. Full IPv6 with prefix delegation from your ISP requires adding DHCPv6-PD config to your WAN network unit and advertising prefixes on the LAN — a topic that warrants its own guide.
Why use dnsmasq instead of ISC DHCP + BIND?
dnsmasq integrates DHCP and DNS caching in one small daemon, requires minimal config, and automatically inserts DHCP hostnames into its DNS answers. ISC DHCP + BIND is appropriate for large networks where you need split-horizon DNS, dynamic updates, or DNSSEC signing.
How do I block specific sites or ad domains at the router level?
dnsmasq supports block lists via 'address=/doubleclick.net/#' entries or by pointing to a hosts-format block list with the 'addn-hosts' directive. Tools like Pi-hole use this same mechanism and can replace the dnsmasq DNS function if you want a web UI.
Will nftables rules survive if I also have firewalld or ufw installed?
No. firewalld and ufw both manage nftables or iptables rules independently. Running them alongside a manual /etc/nftables.conf will cause conflicts. Disable and mask firewalld or ufw before using the approach in this guide: 'sudo systemctl disable --now firewalld'.

Related guides