A multi-hop VPN routes your traffic through two servers in two different countries, so no single machine ever knows both who you are and what you are doing. Commercial VPNs offer this as "Double VPN" or "Secure Core", but the most private version is one you build yourself from two servers only you control. This guide chains two WireGuard servers - an entry server you connect to and an exit server your traffic leaves from - so your real IP is hidden behind two hops that nobody else can log.
How the chain works
Your device connects only to the entry server. The entry server does not send your traffic straight to the internet - instead it forwards everything through a second WireGuard tunnel to the exit server, which then reaches the internet. So the path is: you → entry → exit → internet. The entry server sees your real IP but never your destination; the exit server sees your destination but only ever sees the entry server. Websites see the exit server's IP. We will give the exit server the address range 10.8.0.0/24 and the entry server 10.7.0.0/24 for your devices.
Step 1: Set up the exit server
The exit server is just a normal WireGuard server. On the exit VPS, install WireGuard and generate its keys:
apt update && apt install -y wireguard
wg genkey | tee /etc/wireguard/exit.key | wg pubkey > /etc/wireguard/exit.pub
cat /etc/wireguard/exit.key # this is EXIT_PRIVATE_KEY
cat /etc/wireguard/exit.pub # this is EXIT_PUBLIC_KEY
Find your public network interface (usually eth0) with ip route show default. Then create /etc/wireguard/wg0.conf on the exit server:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = EXIT_PRIVATE_KEY
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# The entry server (we add its key in Step 4)
PublicKey = ENTRY_LINK_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32
Replace eth0 if your interface differs. Leave ENTRY_LINK_PUBLIC_KEY as a placeholder for now - we generate it in Step 4.
Step 2: Turn on forwarding and start the exit
Enable IP forwarding permanently, then start WireGuard on the exit server:
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-wg.conf
sysctl -p /etc/sysctl.d/99-wg.conf
ufw allow 51820/udp
systemctl enable --now wg-quick@wg0
Note the exit server's public IP - we will call it EXIT_IP.
Step 3: Set up the entry server (your side)
On the entry VPS, install WireGuard and generate two key pairs - one for the tunnel that faces your devices (wg0) and one for the link to the exit (wg1):
apt update && apt install -y wireguard
wg genkey | tee /etc/wireguard/entry0.key | wg pubkey > /etc/wireguard/entry0.pub # faces your devices
wg genkey | tee /etc/wireguard/entry1.key | wg pubkey > /etc/wireguard/entry1.pub # link to exit
The public key in entry1.pub is the ENTRY_LINK_PUBLIC_KEY you left as a placeholder on the exit server in Step 1 - go back and paste it into the exit's wg0.conf now, then run systemctl restart wg-quick@wg0 on the exit server.
Step 4: Create the entry-to-exit link (wg1)
On the entry server, create /etc/wireguard/wg1.conf. This makes the entry server a client of the exit server:
[Interface]
Address = 10.8.0.2/24
PrivateKey = ENTRY_LINK_PRIVATE_KEY
# Do NOT route the whole server through the exit - that would cut your own SSH.
# Send only your client subnet (10.7.0.0/24) through the exit, via a separate table.
Table = off
PostUp = ip rule add from 10.7.0.0/24 table 120; ip route add default dev wg1 table 120
PostDown = ip rule del from 10.7.0.0/24 table 120; ip route del default dev wg1 table 120
[Peer]
# The exit server
PublicKey = EXIT_PUBLIC_KEY
Endpoint = EXIT_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
ENTRY_LINK_PRIVATE_KEY is the contents of entry1.key. This is the most important step to get right. A plain AllowedIPs = 0.0.0.0/0 would make WireGuard route the entry server's own traffic - including your SSH session - through the exit, which breaks your access to the server. Table = off stops that, and the policy-routing rules send only your client subnet through the exit, leaving the server itself reachable as normal.
Step 5: Create the entry tunnel for your devices (wg0)
Still on the entry server, create /etc/wireguard/wg0.conf. This is the tunnel your phone or laptop connects to, and the routing rules push that traffic into wg1:
[Interface]
Address = 10.7.0.1/24
ListenPort = 51820
PrivateKey = ENTRY_DEVICE_PRIVATE_KEY
PostUp = iptables -A FORWARD -i wg0 -o wg1 -j ACCEPT; iptables -A FORWARD -i wg1 -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o wg1 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o wg1 -j ACCEPT; iptables -D FORWARD -i wg1 -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o wg1 -j MASQUERADE
[Peer]
# Your device (we add its key in Step 6)
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.7.0.2/32
ENTRY_DEVICE_PRIVATE_KEY is the contents of entry0.key. The masquerade on wg1 hides your device behind the entry server, so the exit only ever sees the entry.
Step 6: Create your device config and connect
Generate a key pair for your device (on your computer, or on the entry server), then build the client config. Put the device's public key into the entry's wg0.conf where it says CLIENT_PUBLIC_KEY. Your device config looks like this:
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.7.0.2/32
DNS = 1.1.1.1
[Peer]
PublicKey = ENTRY_DEVICE_PUBLIC_KEY
Endpoint = ENTRY_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Here ENTRY_DEVICE_PUBLIC_KEY is from entry0.pub, and ENTRY_IP is the entry server's public IP - the only server your device ever talks to.
Step 7: Start the entry and verify the chain
On the entry server, enable forwarding and start both tunnels in order (the link first, then the device tunnel):
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-wg.conf
sysctl -p /etc/sysctl.d/99-wg.conf
ufw allow 51820/udp
systemctl enable --now wg-quick@wg1
systemctl enable --now wg-quick@wg0
Now import the client config into the WireGuard app on your device and connect. To confirm the chain works, check your IP - it should show the exit server's country, not the entry's:
curl https://api.ipify.org
If that returns the exit server's IP, your traffic is hopping through both servers. Run a leak test on our network tools page to be sure there are no DNS or WebRTC leaks.
sysctl net.ipv4.ip_forward should return 1) on both servers, that the exit server has your entry key in its [Peer] block, and that both firewalls allow UDP 51820. If the IP shows the entry country instead of the exit, the MASQUERADE -o wg1 rule on the entry did not load - restart wg0 on the entry server.Is it worth it?
A self-hosted multi-hop is the most private setup there is: two servers you own, in two countries, with no provider in the middle keeping logs. The trade-offs are speed (two hops add latency) and cost (two VPS instead of one). For everyday browsing a single server is plenty; build the chain when your threat model genuinely calls for it. And remember: a multi-hop protects your privacy, it does not change the law of wherever you live.