Linux guide
What you get from us
Replace these placeholders with the details you received with your FixedIP.be order.
PRIVATE_KEY | the private key of your tunnels (keep it secret) |
PORT | the UDP port of our tunnel servers |
ENDPOINT_1 | the address of our tunnel server in Antwerp |
SERVER_PUBLIC_KEY_1 | the public key of our tunnel server in Antwerp |
TUNNEL_IP_1 | the tunnel address of your router towards Antwerp |
SERVER_TUNNEL_IP_1 | the tunnel address on our side in Antwerp (gateway) |
ENDPOINT_2 | the address of our tunnel server in Zaventem |
SERVER_PUBLIC_KEY_2 | the public key of our tunnel server in Zaventem |
TUNNEL_IP_2 | the tunnel address of your router towards Zaventem |
FIXED_IP | your fixed IPv4 address |
FIXED_SUBNET | your IPv4 subnet, for example /29 |
IPV6_PREFIX | your IPv6 subnet |
IPV6_LAN | a /64 from your IPv6 subnet for your network |
TUNNEL_IP6_1 | the IPv6 tunnel address of your router towards Antwerp |
TUNNEL_IP6_2 | the IPv6 tunnel address of your router towards Zaventem |
LAN_HOST | the 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,PORTand the keys. Allow outgoing UDP traffic toPORT. - Handshake OK, but connections hang: set
MTU = 1360in 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 rulethat the rules are in place, and if needed setnet.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.