← All guides

Linux guide

What you get from us

Replace these placeholders with the details you received with your FixedIP.be order.

PRIVATE_KEYthe private key of your tunnels (keep it secret)
PORTthe UDP port of our tunnel servers
ENDPOINT_1the address of our tunnel server in Antwerp
SERVER_PUBLIC_KEY_1the public key of our tunnel server in Antwerp
TUNNEL_IP_1the tunnel address of your router towards Antwerp
SERVER_TUNNEL_IP_1the tunnel address on our side in Antwerp (gateway)
ENDPOINT_2the address of our tunnel server in Zaventem
SERVER_PUBLIC_KEY_2the public key of our tunnel server in Zaventem
TUNNEL_IP_2the tunnel address of your router towards Zaventem
FIXED_IPyour fixed IPv4 address
FIXED_SUBNETyour IPv4 subnet, for example /29
IPV6_PREFIXyour IPv6 subnet
IPV6_LANa /64 from your IPv6 subnet for your network
TUNNEL_IP6_1the IPv6 tunnel address of your router towards Antwerp
TUNNEL_IP6_2the IPv6 tunnel address of your router towards Zaventem
LAN_HOSTthe internal address of your server or NAS, for example 192.168.1.10

This guide sets up your fixed IP directly on a Linux server (Debian, Ubuntu, Fedora, ...). The server builds two tunnels, one to each PoP, and fails over automatically if one of them drops. Only the traffic to and from your fixed IP runs through the tunnels; the rest of the server's traffic keeps going over your regular connection.

Do you want to use a Linux machine as the router for your whole network? Then also read Linux as a router at the bottom.

1. Installing WireGuard

# Debian / Ubuntu
sudo apt install wireguard-tools nftables

# Fedora / RHEL
sudo dnf install wireguard-tools nftables

2. The two tunnels

Create /etc/wireguard/fixedip1.conf:

[Interface]
PrivateKey = PRIVATE_KEY
Address = TUNNEL_IP_1/32
ListenPort = 51821
MTU = 1420
# No routes: step 3 decides which traffic goes through the tunnel.
Table = off

[Peer]
PublicKey = SERVER_PUBLIC_KEY_1
Endpoint = ENDPOINT_1:PORT
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

And /etc/wireguard/fixedip2.conf, with the same key:

[Interface]
PrivateKey = PRIVATE_KEY
Address = TUNNEL_IP_2/32
ListenPort = 51822
MTU = 1420
Table = off

[Peer]
PublicKey = SERVER_PUBLIC_KEY_2
Endpoint = ENDPOINT_2:PORT
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

Protect the files and start both tunnels:

sudo chmod 600 /etc/wireguard/fixedip1.conf /etc/wireguard/fixedip2.conf
sudo systemctl enable --now wg-quick@fixedip1 wg-quick@fixedip2

3. Your fixed IP and the routing

The fixed IP goes on its own dummy interface, separate from both tunnels. Replies go back through the tunnel the connection came in on; new traffic from your fixed IP goes through whichever tunnel is working. Create /usr/local/sbin/fixedip-up:

#!/bin/sh
# Fixed IP on its own interface
ip link add fixedip0 type dummy 2>/dev/null
ip addr replace FIXED_IP/32 dev fixedip0
ip link set fixedip0 up

# Replies through the tunnel the connection came in on
nft -f - <<'NFT'
table inet fixedip
delete table inet fixedip
table inet fixedip {
    chain pre {
        type filter hook prerouting priority mangle; policy accept;
        iifname "fixedip1" ct state new ct mark set 1
        iifname "fixedip2" ct state new ct mark set 2
    }
    chain out {
        type route hook output priority mangle; policy accept;
        ct mark != 0 meta mark set ct mark
    }
}
NFT
ip route replace default dev fixedip1 table 101
ip route replace default dev fixedip2 table 102
ip rule del fwmark 1 table 101 2>/dev/null; ip rule add fwmark 1 table 101 priority 100
ip rule del fwmark 2 table 102 2>/dev/null; ip rule add fwmark 2 table 102 priority 101
ip rule del from FIXED_IP table 100 2>/dev/null; ip rule add from FIXED_IP table 100 priority 102

# New traffic: PoP 1 if it replies, otherwise PoP 2
/usr/local/sbin/fixedip-failover

And /usr/local/sbin/fixedip-failover:

#!/bin/sh
if ping -c 2 -W 2 -I fixedip1 SERVER_TUNNEL_IP_1 >/dev/null 2>&1; then
    ip route replace default dev fixedip1 table 100
else
    ip route replace default dev fixedip2 table 100
fi

Make both executable and have them run automatically:

sudo chmod 755 /usr/local/sbin/fixedip-up /usr/local/sbin/fixedip-failover

/etc/systemd/system/fixedip.service:

[Unit]
Description=fixedip.be fixed IP
After=wg-quick@fixedip1.service wg-quick@fixedip2.service
Requires=wg-quick@fixedip1.service wg-quick@fixedip2.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/fixedip-up

[Install]
WantedBy=multi-user.target

/etc/systemd/system/fixedip-failover.timer (with a .service of the same name that runs ExecStart=/usr/local/sbin/fixedip-failover, Type=oneshot):

[Unit]
Description=fixedip.be failover check

[Timer]
OnBootSec=30
OnUnitActiveSec=15

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now fixedip.service fixedip-failover.timer

4. Your services

A service that listens on all addresses (0.0.0.0 or ::) is immediately reachable on your fixed IP. If you want a service to listen only on your fixed IP, bind it to FIXED_IP.

Don't forget your firewall. With ufw, for example:

sudo ufw allow in on fixedip1 to FIXED_IP port 443 proto tcp
sudo ufw allow in on fixedip2 to FIXED_IP port 443 proto tcp

Allow ping on the tunnels (the default with ufw): our tunnel servers ping TUNNEL_IP_1 and TUNNEL_IP_2 to know which tunnel works. Without a reply, we don't send your fixed IP to that tunnel.

5. Outgoing traffic from your fixed IP too (optional)

For a single program, you choose the source address yourself:

curl --interface FIXED_IP https://ifconfig.me

Does all the server's outgoing traffic need to go over your fixed IP? Then add a default route with src FIXED_IP in both branches at the end of fixedip-failover, for example ip route replace default dev fixedip1 src FIXED_IP metric 50. Bear in mind that the server will then also do its updates and DNS traffic through the tunnels.

6. IPv6

In both configuration files, add the IPv6 tunnel address to Address (TUNNEL_IP6_1/128 and TUNNEL_IP6_2/128), put an address from your subnet on fixedip0 and add the same rules for IPv6:

ip -6 addr replace IPV6_LAN::1/128 dev fixedip0
ip -6 route replace default dev fixedip1 table 101
ip -6 route replace default dev fixedip2 table 102
ip -6 rule add fwmark 1 table 101 priority 100
ip -6 rule add fwmark 2 table 102 priority 101
ip -6 rule add from IPV6_PREFIX table 100 priority 102

and in fixedip-failover, each time also ip -6 route replace default dev fixedip1 table 100 (or fixedip2).

Linux as a router

Are you using the machine as the router for your network? Then enable forwarding (net.ipv4.ip_forward=1) and add a DNAT rule from FIXED_IP to LAN_HOST. Because the replies from LAN_HOST come in from the network, you also need the marking in prerouting:

nft add chain inet fixedip nat '{ type nat hook prerouting priority dstnat; }'
nft add rule inet fixedip nat ip daddr FIXED_IP dnat ip to LAN_HOST
nft add rule inet fixedip pre ct mark != 0 meta mark set ct mark

Do you have a subnet (FIXED_SUBNET)? Then put it on a LAN interface and replace from FIXED_IP with from FIXED_SUBNET. The devices then use their public address directly, without NAT.

Checking

sudo wg show
ip rule
ip route show table 100
curl --interface FIXED_IP https://ifconfig.me

For both tunnels, latest handshake should show a recent time, table 100 points to fixedip1, and the curl command returns your fixed IP.

Testing failover: sudo systemctl stop wg-quick@fixedip1. Within half a minute table 100 points to fixedip2 and your fixed IP stays reachable. Then start the tunnel again.

Rolling back

sudo systemctl disable --now fixedip-failover.timer fixedip.service wg-quick@fixedip1 wg-quick@fixedip2
sudo ip link del fixedip0
sudo nft delete table inet fixedip
sudo rm /etc/wireguard/fixedip1.conf /etc/wireguard/fixedip2.conf /usr/local/sbin/fixedip-up /usr/local/sbin/fixedip-failover
sudo rm /etc/systemd/system/fixedip.service /etc/systemd/system/fixedip-failover.*

Problems?

  • No handshake: check ENDPOINT_1/ENDPOINT_2, PORT and the keys. Allow outgoing UDP traffic to PORT.
  • Handshake OK, but connections hang: set MTU = 1360 in both files. On 4G/5G and some fibre or Starlink connections, 1420 is too much.
  • Incoming packets arrive but nothing goes back: check with ip rule that the rules are in place, and if needed set net.ipv4.conf.all.rp_filter=2.

Stuck? E-mail us at info@fixedip.be

Ready for your fixed IP?

Start today and be reachable everywhere.