SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

WireGuard VPN zelf hosten op een Linux VPS

Bouw uw eigen WireGuard VPN op een Linux VPS. Leer hoe u wg0.conf configureert, IP forwarding inschakelt en veelvoorkomende handshake fouten oplost voor een veilige verbinding.

Wat u bouwt

Een WireGuard VPN op een server in eigen beheer bestaat uit ongeveer veertig regels configuratie: één sleutelpaar, één interfacebestand, één sysctl-instelling, één NAT-regel en één firewall-poort. De installatie is triviaal, dus dit artikel richt zich grotendeels op mogelijke problemen, sleutelrechten, AllowedIPs, forwarding en DNS.

WireGuard is een Layer 3-tunnel in de kernel, die sinds Linux 5.6 onderdeel is van de mainline. Ubuntu 24.04 en Debian 13 leveren dit standaard zonder externe modules. Er is geen cipher-onderhandeling, geen certificaatautoriteit en geen gebruikersnaam/wachtwoord-stap: een peer is simpelweg een publieke sleutel plus de IP-adressen die deze sleutel mag gebruiken. Een pakket dat niet door de MAC-controle komt, wordt zonder antwoord genegeerd, waardoor de poort niet reageert op scans. Het nadeel hiervan is dat er geen centrale authenticatieserver bestaat; het intrekken van toegang vereist dus het verwijderen van de peer op de server zelf.

Controleer eerst de virtualisatie

WireGuard vereist een kernel waarin u een module kunt laden; op een KVM VPS werkt dit direct. Bij containervirtualisatie die de host-kernel deelt, zoals OpenVZ of LXC, faalt het eerste commando met RTNETLINK answers: Operation not supported en is de fallback de wireguard-go userspace-implementatie. Controleer dit eerst met sudo modprobe wireguard && echo ok.

Genereer sleutels zonder deze te lekken

Een /etc/wireguard/server.key die voor iedereen leesbaar is, staat gelijk aan het ontbreken van een VPN. De gebruikelijke umask 077 && wg genkey | sudo tee ...-regel is onbetrouwbaar, omdat sudo een eigen umask toepast op het bestand dat tee aanmaakt. Stel de modus expliciet in.

sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.key

Genereer het client-paar op dezelfde wijze. wg genpsk voegt een optionele pre-shared key toe, bestaande uit één regel in elke configuratie.

De serverinterface: /etc/wireguard/wg0.conf

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>

[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32

chmod 600 het; een waarschuwing bij het opstarten dat het bestand voor iedereen toegankelijk is, betekent dat u die stap heeft overgeslagen. Address is het adres van de server binnen de tunnel, inclusief het masker van het gehele VPN-subnet. Kies een bereik dat u in de praktijk niet zult tegenkomen; 192.168.1.0/24 botst met de helft van de thuisrouters waar uw clients achter zitten, waardoor de tunnel zonder foutmeldingen het verkeer verliest aan de lokale route.

De AllowedIPs van een peer aan de serverzijde is een /32, het specifieke tunneladres dat de client bezit. Als u twee peers hetzelfde AllowedIP geeft, wordt het verkeer verplaatst naar de laatst geconfigureerde peer, terwijl de eerste stopt met het ontvangen van verkeer zonder dat er ergens een foutmelding verschijnt. Laat SaveConfig oningesteld, anders overschrijft wg-quick down dit bestand op basis van de actieve status.

De server als router configureren

Een Linux-server negeert pakketten die niet aan het eigen adres zijn gericht. IP-forwarding en source NAT zijn standaard uitgeschakeld.

printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
  | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward

Een kale sysctl -w werkt tot de volgende herstart en stopt daarna zonder melding. Voor NAT is de egress-interface nodig, de netwerkkaart die verbinding maakt met het internet, niet wg0. Ga niet uit van eth0; bepaal de juiste interface via ip route show default, aangezien moderne images namen gebruiken zoals enp1s0 of ens3.

Firewall: the port, and the forward path

One nftables file covers filter and NAT. Write /etc/nftables.conf, it flushes the existing ruleset, so skip this on a box already managed by ufw or Docker.

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    iif lo accept
    tcp dport 22 accept
    udp dport 51820 accept
  }
  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state established,related accept
    iifname "wg0" oifname "enp1s0" accept
  }
}

table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
  }
}

Apply it with sudo systemctl enable --now nftables, keeping a second SSH session open: policy drop plus a typo in the SSH rule locks you out of your own server. Note what the forward chain does not allow, wg0 to wg0. Peers reach the internet, not each other; add iifname "wg0" oifname "wg0" accept for a peer-to-peer VPN. The same chain governs what a peer may touch on the server itself, which matters when the box doubles as a remote development box running Claude Code in tmux and you would rather not expose that side of it publicly.

On a ufw box: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" in /etc/default/ufw, and a *nat POSTROUTING MASQUERADE rule at the top of /etc/ufw/before.rules.

Activeer het onder systemd

sudo systemctl enable --now wg-quick@wg0
sudo wg show

wg-quick maakt de interface aan, voegt de adressen toe en installeert routes die zijn afgeleid van AllowedIPs. enable --now is het belangrijke onderdeel: een handmatig uitgevoerde wg-quick up wg0 verdwijnt na de volgende herstart, en kernel-upgrades betekenen herstarts. Een unit die na zo'n herstart niet terugkeert, blijft stil totdat iemand probeert verbinding te maken. Daarom is een OnFailure= drop-in op wg-quick@wg0 die verwijst naar uw eigen ntfy server de goedkoopste manier om hierover op uw telefoon bericht te krijgen in plaats van via een buitengesloten gebruiker.

De clientconfiguratie, en de instelling die iedereen verkeerd begrijpt

[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

AllowedIPs voert twee verschillende taken tegelijk uit, en het verwarren van deze twee is de bron van de meeste onduidelijkheid over WireGuard.

Uitgaand is het een routeringstabel. Een pakket waarvan de bestemming overeenkomt met de AllowedIPs van een peer, wordt versleuteld en naar die peer verzonden. 0.0.0.0/0, ::/0 stuurt alles door de tunnel, een volledige tunnel, waarbij de server als standaardroute fungeert. Een split-tunnel is een beperktere lijst: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 draagt VPN-verkeer plus één privénetwerk achter de server, terwijl al het overige verkeer de lokale route behoudt. Die beperkte lijst stelt u in staat om services volledig buiten het openbare internet te houden, zoals een private Nextcloud-instantie op een VPS die gebonden is aan het tunneladres, of de nested-virtualisation lab-VM's die op dezelfde machine draaien; deze blijven bereikbaar voor peers en onzichtbaar voor iedereen anders.

Inkomend is het een toegangscontrolelijst. Een ontsleuteld pakket van een peer waarvan het bronadres niet in de AllowedIPs van die peer staat, wordt geweigerd. Daarom vermeldt de server 10.8.0.2/32 voor de laptop: een invoer van 0.0.0.0/0 zou die client in staat stellen om elk adres in de tunnel te spoofen.

PersistentKeepalive is bedoeld voor clients achter NAT, waarbij de router de UDP-mapping alleen openhoudt zolang er pakketten worden verzonden. Wanneer deze verloopt, kan de server de client niet langer bereiken. PersistentKeepalive = 25 houdt de mapping open; stel dit in op de client, niet op een server met een openbaar IP-adres.

DNS, en het lek dat niemand opmerkt

Met AllowedIPs = 0.0.0.0/0 en geen DNS =-regel behoudt de client de resolver die hij heeft geleerd van het lokale netwerk, de router in het café op 192.168.1.1. Die route is specifieker dan de standaardroute, waardoor DNS-query's via de lokale verbinding in leesbare tekst (cleartext) worden verstuurd, terwijl al het overige verkeer wordt getunneld. Het verkeer is privé; de lijst met namen niet.

Er zijn twee eerlijke opties. Wijs DNS naar een publieke resolver (DNS = 9.9.9.9) en de query's reizen door de tunnel en verlaten deze via uw server, al ziet die resolver ze nog steeds. Of draai unbound of dnsmasq gebonden aan 10.8.0.1, stel DNS = 10.8.0.1 in en voeg udp dport 53 iifname "wg0" accept toe aan de input-chain, stel die regel in en vergeet de resolver, en er wordt helemaal niets meer opgelost.

Op Linux-clients past wg-quick de DNS toe via resolvconf; als deze ontbreekt, krijgt u resolvconf: command not found. Installeer openresolv, of stel PostUp = resolvectl dns %i 10.8.0.1 in op een systemd-resolved client.

Peers toevoegen en verwijderen zonder de tunnel te verbreken

Het herstarten van de interface verbreekt de verbinding voor alle actieve gebruikers. Voeg het [Peer]-blok toe aan wg0.conf en laad vervolgens de peer-set opnieuw in terwijl de verbinding actief blijft.

sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'

wg-quick strip toont de configuratie zonder de wg-quick-specifieke sleutels (Address, DNS, PostUp), en syncconf past het verschil toe terwijl actieve sessies behouden blijven. Dit werkt alleen voor peers: een gewijzigde Address vereist nog steeds een volledige down/up-cyclus. Trek de toegang in met sudo wg set wg0 peer <public key> remove en verwijder daarna het blok uit het bestand, anders keert de configuratie terug bij de volgende herlaadactie.

Foutmodi en bijbehorende meldingen

Handshake wordt niet voltooid. wg show toont de peer zonder latest handshake, en de client logt:

Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)

Er komt niets aan, of er wordt niets geaccepteerd. Controleer in deze volgorde: staat UDP 51820 open in de firewall van de VPS en in de netwerkfirewall van uw provider (dit is bij de meeste beheerpanelen een aparte instelling); zijn het Endpoint-adres en de poort correct; zijn de keys verwisseld. De key in het [Peer]-blok van de client moet de public key van de server zijn, en vice versa. Het invoeren van een private key of de eigen public key van de client resulteert precies in dit symptoom. sudo tcpdump -ni any udp port 51820 op de server laat zien of er überhaupt pakketten aankomen. De kernelmodule logt standaard niets; WireGuard-berichten verschijnen pas in dmesg nadat u dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) hebt ingeschakeld. Met die optie aan, verschijnt een key-mismatch als een invalid-MAC drop.

Handshake werkt, geen internet. ping 10.8.0.1 slaagt, maar ping 1.1.1.1 geeft een timeout: forwarding of NAT ontbreekt. Controleer of sysctl net.ipv4.ip_forward op 1 staat en bekijk de tellers terwijl de client pingt, met sudo nft list ruleset of sudo iptables -t nat -L POSTROUTING -n -v. Nul pakketten op de masquerade-regel betekent dat de naam van de egress-interface onjuist is; een oplopende teller zonder antwoorden wijst op de policy van de forward-chain.

Internet werkt, namen niet. ping 1.1.1.1 slaagt en curl https://example.com geeft Could not resolve host terug. De DNS-regel ontbreekt, of deze verwijst naar een resolver die onbereikbaar is vanuit de tunnel.

Sommige HTTPS-sites blijven hangen. SSH en ping werken prima; grote pagina's lopen vast. Dit is path MTU: de tunnel voegt overhead toe en een tussenliggende verbinding laat de te grote pakketten vallen zonder dat er een ICMP-bericht terugkomt. Verlaag MTU in de [Interface] van de client, probeer 1420, daarna 1380, en vervolgens 1280. Als het verlagen van de MTU de vastlopers verhelpt maar de doorvoer nog steeds tegenvalt, stop dan met gokken en volg de werkelijke path MTU bepalen via bisection en TCP MSS-clamping. Hiermee sluit u ook oorzaken uit die niets met de tunnel te maken hebben.

Interface weigert te starten. Address already in use betekent dat een ander proces UDP 51820 bezet houdt. Cannot find device wg0 na een mislukte up betekent meestal dat de configuratie is afgewezen; lees journalctl -u wg-quick@wg0 -n 50.

Migreren van Streisand of OpenVPN

Streisand wordt niet langer onderhouden en de repository is gearchiveerd. Het draaien van een VPN op verouderde automatisering vormt een sluimerend beveiligingsrisico. Er is geen mogelijkheid voor een in-place upgrade en de PKI van OpenVPN is niet converteerbaar: WireGuard gebruikt geen certificaten, geen CA en kent geen verloopdata, waardoor elke client een nieuw sleutelpaar krijgt.

Migreer parallel; WireGuard op UDP 51820 kan naast OpenVPN op 1194 op dezelfde server draaien. Zet wg0 op, verplaats clients één voor één en stop daarna de oude service. Het gebruikersnaam/wachtwoord-model en de intrekkingsprocedure van OpenVPN zijn niet overdraagbaar; als u accounts of een audit-trail nodig heeft, dient u dit als laag bovenop WireGuard te implementeren.

Backups, upgrades en schaalbaarheid

/etc/wireguard is de server. Maak er een back-up van (sudo tar czf wg-backup.tgz -C /etc wireguard, modus 600, bewaard buiten de server) en u kunt binnen enkele minuten een nieuwe VPS herstellen. Verliest u de private key van de server, dan moet elke clientconfiguratie opnieuw worden uitgegeven, aangezien clients de public key van de server verifiëren. Upgrades zijn een standaard apt upgrade, gevolgd door een reboot voor kernel-updates; wg-quick@wg0 start automatisch opnieuw op als u dit heeft ingeschakeld.

De status per peer is beperkt en de cryptografie draait in de kernel. De limiet wordt daarom bepaald door de CPU en bandbreedte van uw VPS, niet door deze configuratie. Meet dit met iperf3 over de tunnel in plaats van af te gaan op gepubliceerde cijfers. Bij schaalvergroting is het beheer de grootste uitdaging. Elke peer heeft een uniek tunnel-IP nodig. Het handmatig bewerken van zestig [Peer]-blokken leidt onvermijdelijk tot dubbele AllowedIPs: genereer de configuraties daarom met een script. Eén server is één UDP-eindpunt en één single point of failure; WireGuard ondersteunt geen clustering. Redundantie vereist een tweede server met eigen keys. Key-rotatie blijft handmatig werk, dus houd nauwkeurig bij wie welke key bezit en hoe u deze intrekt. Zodra deze administratie te complex wordt voor een tekstbestand, is de gebruikelijke oplossing een control plane boven op hetzelfde kernel-datavlak. Een zelfgehoste NetBird-server beheert de adresallocatie, peer-distributie en setup-keys die u anders handmatig zou uitvoeren. Als het draaien van die control plane te veel werk is, host Tailscale er een voor u. Het gratis abonnement ondersteunt zes gebruikers met een onbeperkt aantal apparaten, wat voor de meeste persoonlijke netwerken volstaat. Voorbij dat punt betaalt u per persoon in plaats van per machine. Daarom hangt wat een huishouden of klein team daadwerkelijk betaalt af van het aantal gebruikers met een login, niet van het aantal peers dat u anders handmatig in wg0.conf zou invoeren. Aan die kant wordt de administratie van split-tunnel AllowedIPs het adverteren van uw private ranges via een subnet router, wat eenmalig vanaf één VPS wordt aangekondigd en centraal wordt goedgekeurd in plaats van in elk clientbestand te worden geplakt. Of die afweging de moeite waard is, hangt af van wat een gehoste control plane kan bereiken; deze bezit nooit de keys die uw verkeer versleutelen, maar bepaalt wel welke peers van elkaar op de hoogte zijn.

Dit alles vereist een Linux-server die u beheert, een publiek IP-adres, een kernel waarin u een module kunt laden en een firewall die u volledig onder controle heeft.

FAQ

Waarom wordt de WireGuard-handshake nooit voltooid?

wg show het vermelden van een peer zonder latest handshake betekent dat pakketten niet aankomen of niet worden geaccepteerd. Controleer UDP 51820 op zowel de VPS-firewall als de externe netwerkfirewall van uw provider, bevestig de Endpoint host en poort, en controleer vervolgens of de sleutels niet zijn verwisseld; het [Peer] blok van de client moet de publieke sleutel van de server bevatten. sudo tcpdump -ni any udp port 51820 op de server toont of er überhaupt pakketten aankomen; dmesg rapporteert alleen WireGuard-handshakefouten nadat u dynamische debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) hebt ingeschakeld, waarna een sleutel-mismatch verschijnt als een invalid-MAC drop.

De tunnel maakt verbinding, maar ik heb geen internet. Wat ontbreekt er?

ping 10.8.0.1 werkt terwijl ping 1.1.1.1 een time-out geeft, wat wijst op problemen met forwarding of NAT. Bevestig dat sysctl net.ipv4.ip_forward de waarde 1 bevat en dat dit is ingesteld in /etc/sysctl.d/, en niet alleen met een sysctl -w die na een reboot verdwijnt. Controleer vervolgens de masquerade-regel die uw werkelijke uitgaande interface benoemt vanuit ip route show default, enp1s0 of ens3, zelden eth0.

Heb ik de DNS = regel nodig in mijn clientconfiguratie?

Bij een volledige tunnel zonder DNS = regel behoudt de client de resolver die hij van het lokale netwerk heeft geleerd, en die queries verlaten het netwerk in platte tekst via de lokale verbinding terwijl al het andere verkeer door de tunnel gaat. Wijs DNS naar een publieke resolver, of draai unbound/dnsmasq gebonden aan 10.8.0.1 en open udp dport 53 iifname "wg0" in de input-chain.

Wat regelt AllowedIPs precies?

Het vervult twee taken. Uitgaand fungeert het als een routeringstabel: verkeer dat overeenkomt met de AllowedIPs van een peer wordt versleuteld en naar die peer verzonden. Inkomend fungeert het als een toegangscontrolelijst: een ontsleuteld pakket waarvan de bron buiten de AllowedIPs van die peer valt, wordt geweigerd. Daarom vermeldt de serverzijde één /32 per client, terwijl de clientzijde 0.0.0.0/0 kan vermelden.

Werkt WireGuard op elke VPS?

Op een KVM VPS werkt het met de in-kernel module zonder extra configuratie. Bij containervirtualisatie die de host-kernel deelt, zoals OpenVZ of LXC, faalt modprobe wireguard met Operation not supported en is de fallback de wireguard-go userspace-implementatie. Voer sudo modprobe wireguard && echo ok uit voordat u iets anders doet.