TTR Networks

Intermediate · Two internet connections on Debian · section 1 of 8

Two ISPs, four jobs

✓ Lab-tested 21 October 2026 on Debian 13.7, iproute2 6.15.0, nftables 1.1.3, systemd 257

Give a Debian office router a second ISP: a routing table per ISP, source rules and connection marks so replies leave the way they came, per-ISP NAT, and a systemd health check that fails over and back, measured with a real outage.

By the end you will be able to:

  • Explain why one default route breaks inbound traffic on a second ISP
  • Build per-ISP routing tables and ip rules that route by source address and firewall mark
  • Use nftables connection marks so forwarded replies leave through the ISP they arrived on
  • Write a health check that tests beyond the ISP, run it on a systemd timer, and measure failover and recovery
  • Recognise what a failover can't preserve, and the rp_filter trap

Prerequisites: Linux Network Connectivity, nftables and iptables on Debian: host and router firewalls

A second internet connection is the most common request after a VPN: when the main ISP fails, the office should carry on. On a dedicated router it's a few lines of configuration. On a plain Linux router you build each piece yourself, which is the best way to see what any dual-ISP router has to do:

  1. Send replies back the way the request came in, because each ISP only accepts its own addresses.
  2. Remember which ISP each incoming connection used.
  3. Notice when an ISP stops working, even if its link stays up.
  4. Switch to the other ISP, and back again, without someone logging in.

This lesson builds all four on Debian 13 with routing tables, ip rule, nftables marks and a health check that runs as a systemd timer. It builds on nftables and iptables on Debian for the firewall and NAT, and on the policy routing you saw wg-quick set up in WireGuard on Debian. For the same job on a dedicated router, see Two internet connections on VyOS.

The lab

The office router, gw, has an address from each ISP: 198.51.100.2 from ISP 1 (the main one) and 203.0.113.2 from ISP 2 (the backup). The office LAN is 10.20.0.0/24, and the PC there also runs a web server published through both ISPs on port 8080. Behind the ISPs, a "core" router stands for the rest of the internet, with a web server that logs who visits.

Like real ISPs should (the best practice known as BCP38), each ISP drops packets from its customer that don't carry one of its own addresses. That one realistic detail is what makes a dual-ISP router hard. Every output is from a real run.

Diagram: The dual-ISP lab — An office router with an address from each ISP. Each ISP only accepts its own addresses from the office.
  1. Normally: ISP 1. The main table's default route points at ISP 1, and masquerade gives the office ISP 1's address, 198.51.100.2.
  2. In on ISP 2, out on ISP 1. With one default route, a reply to traffic that arrived on ISP 2 leaves through ISP 1, still carrying ISP 2's address. ISP 1 drops it: it only accepts its own addresses.
  3. Back the way it came. A routing table per ISP, rules that pick one by source address or connection mark, and replies leave through the ISP they arrived on.
  4. ISP 1 fails beyond the link. The link to ISP 1 stays up and its router still answers, but nothing gets through it. Only a check that reaches past the ISP notices.
  5. Failover to ISP 2. After three failed checks, the health check moves the default route to ISP 2, and the office reappears on the internet as 203.0.113.2.
The versions in this lab (real output)
root@gw:~# cat /etc/debian_version; dpkg-query -W nftables iproute2 conntrack systemd; uname -r
13.7
conntrack	1:1.4.8-2+b1
iproute2	6.15.0-1
nftables	1.1.3-1
systemd	257.13-1~deb13u1
7.0.14-linuxkit
  • Line 2: Debian 13 (trixie).
  • Lines 3–6: Everything used here ships with Debian: conntrack-tools, iproute2, nftables and systemd.