What you will set up
A WireGuard server that your phone, laptop or router connects to. While connected, a device:
- browses the internet over IPv4 through your server's address, and
- gets its own public IPv6 address from the /64 that comes with your server.
Every command below was run on a fresh Melonslab server with Debian 13, and the result checked from a connected device.
Before you start
You need:
- a server with Debian 13 and root access,
- an IPv4 address on the server, so devices can reach it from any network, and
- the WireGuard app on each device (for Android, iOS, Windows, macOS and Linux, from wireguard.com).
The examples use 203.0.113.10 for the server's IPv4 address and 2001:db8:1f::/64 for its IPv6 range. Replace them with your own throughout.
1. Install WireGuard
apt update
apt install -y wireguard nftables qrencode
nftables shares the server's IPv4 address with your devices, and qrencode puts a configuration on your phone's screen as a QR code.
2. Find your interface and IPv6 range
ip route show default
ip -6 addr show scope global
The first command shows your network interface after dev, usually eth0. The second shows your IPv6 address and range, for example 2001:db8:1f::a/64. The range is the part before the last ::, here 2001:db8:1f::/64. The examples use eth0; if yours is different, change it everywhere below.
3. Create keys
Every side of the tunnel has a private key, which never leaves it, and a public key, which you give to the other side.
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
wg genkey | tee phone.key | wg pubkey > phone.pub
umask 077 keeps the key files readable by root only.
4. Turn on forwarding
Your server has to pass traffic between the tunnel and the internet:
cat > /etc/sysctl.d/99-wireguard.conf <<EOF
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.eth0.proxy_ndp = 1
EOF
sysctl --system
The last line matters for IPv6. On Melonslab, your /64 is on the network link rather than routed to your server: the router asks on the network which machine has each address. Your server has to answer on behalf of the devices in its tunnel, and proxy_ndp lets it, together with one entry per device that the next step adds. Without it, IPv4 works in the tunnel and IPv6 silently does not.
Your server's own IPv6 route is set statically, so turning on forwarding does not affect it.
5. Configure the server
Create /etc/wireguard/wg0.conf:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
# IPv4: devices share the server's address
PostUp = nft add table ip wireguard
PostUp = nft add chain ip wireguard postrouting "{ type nat hook postrouting priority srcnat; }"
PostUp = nft add rule ip wireguard postrouting ip saddr 10.8.0.0/24 oifname eth0 masquerade
PostDown = nft delete table ip wireguard
# IPv6: each device has its own address from your /64
PostUp = ip -6 neigh add proxy 2001:db8:1f::100 dev eth0
PostDown = ip -6 neigh del proxy 2001:db8:1f::100 dev eth0
[Peer]
# phone
PublicKey = PHONE_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32, 2001:db8:1f::100/128
Replace SERVER_PRIVATE_KEY with the output of cat server.key and PHONE_PUBLIC_KEY with the output of cat phone.pub. The phone gets 10.8.0.2 inside the tunnel and 2001:db8:1f::100 as its public IPv6 address.
6. Start WireGuard
systemctl enable --now wg-quick@wg0
wg show
wg show lists the interface and the phone as a peer. WireGuard now also starts on every boot.
7. Connect your first device
Create /etc/wireguard/phone.conf, the configuration the phone will use:
[Interface]
PrivateKey = PHONE_PRIVATE_KEY
Address = 10.8.0.2/32, 2001:db8:1f::100/128
DNS = 1.1.1.1, 2606:4700:4700::1111
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Replace PHONE_PRIVATE_KEY with cat phone.key and SERVER_PUBLIC_KEY with cat server.pub. AllowedIPs = 0.0.0.0/0, ::/0 sends all of the phone's traffic through the tunnel, and DNS sets the resolvers the WireGuard app uses while connected. To answer those lookups on your own server instead, see the Unbound guide.
To load it on the phone, show it as a QR code and scan it with the WireGuard app:
qrencode -t ansiutf8 < phone.conf
On a laptop, import phone.conf into the WireGuard app instead.
8. Check that it works
Connect, then open ifconfig.co in the device's browser. It shows the address the device is using, normally its own IPv6 address, 2001:db8:1f::100.
If the device is a laptop, you can check both:
curl -4 ifconfig.co
curl -6 ifconfig.co
The first should print your server's IPv4 address, 203.0.113.10, and the second the laptop's own IPv6 address.
Adding more devices
Each device needs its own keys and addresses. For a laptop, create keys:
cd /etc/wireguard
umask 077
wg genkey | tee laptop.key | wg pubkey > laptop.pub
Add a peer to the end of wg0.conf, with the next free addresses:
[Peer]
# laptop
PublicKey = LAPTOP_PUBLIC_KEY
AllowedIPs = 10.8.0.3/32, 2001:db8:1f::101/128
Add the laptop's IPv6 address to the [Interface] section, under the phone's lines:
PostUp = ip -6 neigh add proxy 2001:db8:1f::101 dev eth0
PostDown = ip -6 neigh del proxy 2001:db8:1f::101 dev eth0
Then restart WireGuard, which drops connected devices for a moment:
systemctl restart wg-quick@wg0
The laptop's own configuration is the phone's with its private key and addresses swapped in.
Troubleshooting
The device never connects. wg show on the server shows no "latest handshake" for it. Check that the Endpoint is your server's IPv4 address and that UDP port 51820 reaches the server. Debian does not turn on a firewall by default. If you use ufw as in the security guide, add its two WireGuard rules.
IPv4 works, IPv6 does not. Check that forwarding and proxy_ndp are on, and that the device's address has a proxy entry:
sysctl net.ipv6.conf.all.forwarding net.ipv6.conf.eth0.proxy_ndp
ip -6 neigh show proxy
Each device's IPv6 address should be listed. If one is missing, its PostUp line is missing from wg0.conf.
Running a VPN for customers?
These steps are for a VPN you, your household or your team use. A VPN, proxy or exit service offered to customers needs capacity planned and priced for it, so talk to our sales team before you start.