Practical guide: set up a WireGuard site-to-site tunnel between two Debian VPS

Redes & Segurança · By · Published on · Updated on · 10 min read

How to set up WireGuard site-to-site between two Debian VPS (step-by-step)
Photo: Rafael Minguet Delgado / Pexels

Learn how to configure a WireGuard site-to-site tunnel between two Debian VPS step by step. You’ll install the packages, generate key pairs, create configuration files, open firewall ports, and test the tunnel. This guide focuses on a simple, robust setup you can extend to route internal subnets.

Prerequisites and tunnel topology

We’ll connect two Debian VPS with a dedicated WireGuard tunnel. Each side gets a private tunnel IP and can optionally route internal LAN subnets. You need root or sudo access on both VPS and the WireGuard UDP port reachable through any firewalls.

Before you begin, confirm each VPS public IP, working DNS, synchronized time, and permission to open the UDP port. If you’re still choosing infrastructure, see the guidance on VPS, shared hosting or bare metal and how to choose the right VPS size.

Proposed topology: Site A ↔ Site B via WireGuard, tunnel network 10.100.0.0/24. Each VPS talks to the other through their tunnel IPs. Local subnets behind each VPS are optional.

  • Two Debian VPS with public IPs (Site A and Site B)
  • SSH access with sudo/root
  • WireGuard UDP port reachable (default 51820)
  • Reliable time sync (NTP)
  • Basic firewall knowledge (iptables or UFW)
Item Site A Site B
Public IP <site_a_public_ip> <site_b_public_ip>
Tunnel address 10.100.0.1/24 10.100.0.2/24
UDP port 51820 51820

1) Install WireGuard and enable IPv4 forwarding

Install WireGuard on both servers and enable IPv4 forwarding. Forwarding is required if you plan to route traffic beyond the hosts themselves. Note: updating packages or changing network settings can drop your SSH session — schedule a maintenance window if needed.

# Em A e B
sudo apt update && sudo apt -y upgrade
sudo apt -y install wireguard wireguard-tools

# Verifique as versões e o módulo
wg --version
sudo modprobe wireguard || true

# Habilite IPv4 forwarding de forma persistente
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard-forward.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard-forward.conf

# Opcional: confirme sincronização de horário (NTP)
timedatectl

2) Generate key pairs on both sides

Each end of the tunnel needs a private and public key. Keep the private key on the server and share the public key with the peer. Protect key files with strict permissions; the example below writes keys to /etc/wireguard with restrictive umask.

# Em A e B: gere as chaves privadas e públicas
umask 077
sudo wg genkey | sudo tee /etc/wireguard/privatekey | sudo wg pubkey | sudo tee /etc/wireguard/publickey

# Mostre a chave pública para compartilhar com o outro lado
echo "Chave pública deste host:"
sudo cat /etc/wireguard/publickey

3) Configure Site A (wg0)

Create /etc/wireguard/wg0.conf on Site A. Set the tunnel address, listening port and the peer block pointing to Site B. Use Site B’s public key, public IP, and chosen UDP port.

PersistentKeepalive helps when there’s NAT on the path. Only change MTU if you observe fragmentation or packet loss.

# No Site A — substitua os placeholders antes de rodar
PRIV_A=$(sudo cat /etc/wireguard/privatekey)
PUB_B="<cole_a_chave_publica_do_site_B>"
ENDPOINT_B="<ip_publico_site_b>:51820"

sudo tee /etc/wireguard/wg0.conf > /dev/null <<EOF
[Interface]
Address = 10.100.0.1/24
ListenPort = 51820
PrivateKey = ${PRIV_A}

# Regras básicas de proteção da interface (opcional)
PostUp = sysctl -w net.ipv4.conf.all.rp_filter=2
PostDown = sysctl -w net.ipv4.conf.all.rp_filter=0

[Peer]
PublicKey = ${PUB_B}
AllowedIPs = 10.100.0.2/32
Endpoint = ${ENDPOINT_B}
PersistentKeepalive = 25
EOF

sudo chmod 600 /etc/wireguard/wg0.conf

4) Configure Site B (wg0)

Now create wg0 on Site B, swapping addresses and using Site A’s public key. The Endpoint points to Site A’s public IP and UDP port.

Keep AllowedIPs as narrow as needed to avoid unintended routing and conflicts with local routes.

# No Site B — substitua os placeholders antes de rodar
PRIV_B=$(sudo cat /etc/wireguard/privatekey)
PUB_A="<cole_a_chave_publica_do_site_A>"
ENDPOINT_A="<ip_publico_site_a>:51820"

sudo tee /etc/wireguard/wg0.conf > /dev/null <<EOF
[Interface]
Address = 10.100.0.2/24
ListenPort = 51820
PrivateKey = ${PRIV_B}

PostUp = sysctl -w net.ipv4.conf.all.rp_filter=2
PostDown = sysctl -w net.ipv4.conf.all.rp_filter=0

[Peer]
PublicKey = ${PUB_A}
AllowedIPs = 10.100.0.1/32
Endpoint = ${ENDPOINT_A}
PersistentKeepalive = 25
EOF

sudo chmod 600 /etc/wireguard/wg0.conf

5) Open the UDP port and adjust firewall/NAT

Allow the WireGuard UDP port and permit forwarding between wg0 and the WAN interface. On systems using iptables you can persist rules with iptables-persistent. If you prefer UFW, follow its ruleset — we have an UFW guide on the site.

If you only need the two VPS to talk to each other, NAT is not required. To route internal subnets, see the additional routes section below.

  • Default UDP port 51820 (can change if both sides agree)
  • Allow FORWARD between wg0 and the WAN interface
  • Persist rules across reboots
# Em A e B — descubra a interface WAN
echo "Detectando interface WAN..."
WAN=$(ip route list default | awk '{print $5}' | head -n1)
echo "Interface WAN: ${WAN}"

# iptables (legacy) — abrir porta UDP 51820 e permitir forward
sudo iptables -A INPUT -i ${WAN} -p udp --dport 51820 -j ACCEPT
sudo iptables -A FORWARD -i wg0 -j ACCEPT
sudo iptables -A FORWARD -o wg0 -j ACCEPT

# Persistir regras (Debian)
sudo apt -y install iptables-persistent
sudo netfilter-persistent save

# Alternativa com UFW (se você usa UFW)
# sudo ufw allow 51820/udp
# sudo ufw route allow in on wg0 out on ${WAN}
# sudo ufw route allow in on ${WAN} out on wg0
# sudo ufw status verbose

6) Bring the tunnel up and validate connectivity

With configuration and firewall in place, bring up wg0 and check the handshake. Ping the tunnel IPs to validate connectivity. If the handshake fails, stop and resolve the issue before proceeding.

For additional firewall examples using UFW see How to set up a firewall on Ubuntu with UFW and adapt the recommendations to Debian.

  • Enable and start at boot with wg-quick
  • Check state with wg show
  • Ping between 10.100.0.1 and 10.100.0.2 to test
# Em A e B: subir e habilitar no boot
sudo systemctl enable --now wg-quick@wg0

# Verificar status
sudo wg show
ip addr show wg0

# Testes básicos
# No Site A:
ping -c 4 10.100.0.2
# No Site B:
ping -c 4 10.100.0.1

# Opcional: testar rota
# traceroute 10.100.0.2

7) Troubleshooting and additional routes (optional)

If there’s no handshake, check keys, ports and AllowedIPs. PersistentKeepalive helps with NAT or dynamic IPs. High MTU can cause fragmentation; lower it if you see packet loss or latency issues.

To reach LANs behind either VPS, add the remote subnets to AllowedIPs and, if you can’t add static routes on the far side, use MASQUERADE on wg0 as a last resort.

Plan monitoring and alerts for the tunnel. If you’re still choosing an OS, see guidance on how to choose the right Linux distribution for your server.

  • Verify DNS resolution for Endpoints
  • Confirm the UDP port is actually reachable
  • Check clock/NTP for time drift
  • Adjust MTU if you observe fragmentation
  • Avoid overlapping subnets in AllowedIPs
# Diagnóstico rápido
sudo journalctl -u wg-quick@wg0 -n 100 --no-pager
sudo wg show
sudo ss -lunp | grep 51820 || sudo ss -lun
sudo ip route show table main | grep -E 'wg0|default'
WAN=$(ip route list default | awk '{print $5}' | head -n1)
sudo tcpdump -ni ${WAN} udp port 51820

# Exemplo de wg0.conf com sub-redes atrás dos sites (opcional)
# Suponha LAN_A=10.10.1.0/24 (Site A) e LAN_B=10.10.2.0/24 (Site B)
# Site A (trecho):
# [Interface]
# Address = 10.100.0.1/24
# PostUp = iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -o wg0 -j MASQUERADE
# PostDown = iptables -t nat -D POSTROUTING -s 10.10.1.0/24 -o wg0 -j MASQUERADE
# [Peer]
# PublicKey = <PUB_B>
# AllowedIPs = 10.100.0.2/32, 10.10.2.0/24

# Site B (trecho):
# [Interface]
# Address = 10.100.0.2/24
# PostUp = iptables -t nat -A POSTROUTING -s 10.10.2.0/24 -o wg0 -j MASQUERADE
# PostDown = iptables -t nat -D POSTROUTING -s 10.10.2.0/24 -o wg0 -j MASQUERADE
# [Peer]
# PublicKey = <PUB_A>
# AllowedIPs = 10.100.0.1/32, 10.10.1.0/24

# Após ajustar, reinicie a interface
# sudo systemctl restart wg-quick@wg0

Frequently asked questions

Can I use a port other than 51820 for WireGuard?

Yes — WireGuard listens on any free UDP port as long as both peers use the same value. After choosing a port, open it in any host firewall and update cloud provider security groups or network ACLs that control traffic to your VPS. Also verify NAT and routing allow UDP through; changing the port requires updating the Endpoint on the peer.

What should I put in AllowedIPs?

AllowedIPs should list the destinations you want routed through the tunnel. For a host-to-host setup include the peer’s tunnel IP as a /32. To reach subnets behind the peer, add those network prefixes. Keep entries as narrow as possible to avoid routing traffic unintentionally and to prevent overlaps with local routes or existing networks on either VPS.

What if the VPS public IP changes?

If a VPS public IP can change, put a DNS name in the Endpoint so WireGuard can resolve the peer according to DNS TTL. PersistentKeepalive helps maintain NAT mappings when peers are behind NAT, and restarting or reloading the interface forces a fresh DNS resolution and reconnection after major IP changes. Monitor connectivity to detect failures.

What MTU should I use for WireGuard?

The default WireGuard MTU works for most links. If you observe fragmentation, packet loss, or reduced throughput, lower the MTU and test values in the 1280–1420 range on both peers. Measure with non-fragmented packet tests to validate changes. Only change MTU when you can reproduce issues, since incorrect values may degrade performance.

I use nftables. Will the iptables commands work?

On recent Debian releases iptables may be implemented on top of the nftables backend, so some iptables commands can work, but that depends on your system. Prefer a single firewall tool per server to avoid conflicts. If you manage nftables, translate the iptables rules in this guide into equivalent nftables statements and persist them with your chosen management method.

Is it safe to expose the WireGuard port to the internet?

Exposing the WireGuard UDP port is common; WireGuard uses modern cryptography and will not respond to invalid packets. Security depends on proper configuration: keep the OS and WireGuard updated, protect private keys, use minimal AllowedIPs, and consider restricting source IPs in firewall rules if you need stricter access control. Monitor the tunnel for unexpected traffic.

Need this tunnel adapted for multiple sites or complex routing?

I can review your topology, fine-tune firewall rules, and automate the configuration for production.

Discuss your setup

See also