wg-easy installeren met Docker Compose
Installeer WireGuard met een webinterface via wg-easy. Leer de juiste Docker Compose configuratie, inclusief NET_ADMIN, sysctls en het oplossen van versie 15 instellingen.
Wat u bouwt
wg-easy is WireGuard met een webinterface, uitgevoerd als één Docker-container. Het beheert de WireGuard-interface voor u en voegt een browser-UI toe voor het aanmaken van clients. Elke client die u aanmaakt krijgt een configuratiebestand en een QR-code, zodat een telefoon verbinding maakt met de VPN door de camera op het scherm te richten.
De tunnel zelf is standaard WireGuard. De kernelmodule verplaatst de pakketten, waardoor de doorvoer gelijk is aan een handmatige configuratie. Wat u wint is de client-levenscyclus: peers toevoegen, uitschakelen en verwijderen zonder via SSH een configuratiebestand te hoeven bewerken. Wat u opgeeft is de directe controle over die configuratie, wat het onderwerp is van de handmatige WireGuard-configuratie op een VPS.
U heeft een KVM VPS nodig met een publiek IPv4-adres, Docker Engine met de Compose-plugin en root-toegang. Containervirtualisatie die de host-kernel deelt, zoals OpenVZ of LXC, kan de WireGuard-module meestal niet laden, waardoor de container de interface niet kan opstarten.
Versie 15 heeft de instellingen uit de environment verplaatst
De meeste handleidingen die u zult vinden, zijn geschreven voor wg-easy 14, waarbij u WG_HOST instelde op uw serveradres en PASSWORD_HASH op een bcrypt-hash van het beheerderswachtwoord, beide als environment variables. Versie 15 is een herschreven versie. De officiële migratienotities vermelden duidelijk dat v15 niet dezelfde environment variables gebruikt als v14, en dat de meeste daarvan zijn verplaatst naar het beheerderspaneel in de web-UI.
WG_HOST en PASSWORD_HASH doen daarom niets meer. Als u een oud compose-bestand kopieert, start de container, negeert deze regels en vraagt vervolgens om een beheerdersaccount aan te maken in de browser. Dat is geen bug. Het is de nieuwe installatieprocedure.
Sinds oktober 2026 is de major-tag die u moet vastzetten 15. Pin de major-versie in plaats van latest te gebruiken, omdat een major-upgrade het configuratieformaat op de schijf wijzigt en niet probleemloos teruggedraaid kan worden. De overstap naar 15 verving het JSON-bestand van v14 door een SQLite-database.
Het compose-bestand
Maak een map aan voor de stack en schrijf het officiële compose-bestand hierin. Dit is het upstream-bestand, waarbij de optionele regels die in commentaar staan zijn weggelaten.
sudo mkdir -p /etc/docker/containers/wg-easy
sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml \
https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.ymlDe inhoud ziet er als volgt uit:
volumes:
etc_wireguard:
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
networks:
wg:
ipv4_address: 10.42.42.42
ipv6_address: fdcc:ad94:bacf:61a3::2a
volumes:
- etc_wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
- net.ipv6.conf.default.forwarding=1
networks:
wg:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 10.42.42.0/24
- subnet: fdcc:ad94:bacf:61a3::/64etc_wireguard is een named volume dat de server-key en elke client die u aanmaakt bevat, opgeslagen in de SQLite-database wg-easy.db. Maak een back-up van dit volume, anders raakt u bij een rebuild al uw peers kwijt. Als u deze bestanden liever op het host-bestandssysteem ziet, vervang het dan door een bind mount. Lees eerst het verschil tussen bind mounts en named volumes voordat u dit doet, omdat de permissies zich anders gedragen.
Waarom NET_ADMIN, SYS_MODULE en de sysctls nodig zijn
Een container mag standaard de netwerkstack niet aanpassen, en elk van deze regels heft een specifieke blokkade op.
NET_ADMIN staat de container toe om de wg0 interface aan te maken, een adres toe te wijzen en routes te schrijven. Zonder deze optie start de container en stopt deze direct bij het initialiseren van de interface, omdat ip link add wg0 type wireguard de foutmelding Operation not permitted teruggeeft.
SYS_MODULE in combinatie met de read-only /lib/modules mount stelt de container in staat om de WireGuard kernelmodule te laden als de host deze nog niet heeft geladen. De module bevindt zich in de host-kernel, niet in de image, waardoor de host-directory zichtbaar moet zijn. Op een moderne kernel is de module meestal ingebouwd; u kunt dit controleren met sudo modprobe wireguard && echo ok op de host.
net.ipv4.ip_forward=1 zorgt ervoor dat de kernel pakketten doorstuurt die niet aan de machine zelf zijn geadresseerd. Zonder deze instelling maakt een client verbinding, slaagt de handshake, maar wordt elk pakket naar het internet genegeerd. Hierdoor treedt er een time-out op in ping 1.1.1.1, terwijl de VPN-verbinding er verbonden uitziet.
net.ipv4.conf.all.src_valid_mark=1 is de instelling die vaak voor verrassing zorgt. WireGuard markeert zijn eigen uitgaande pakketten zodat deze niet terug de tunnel in worden gerouteerd. Strikte reverse path filtering ziet een pakket waarvan het bronadres niet overeenkomt met de verwachte route en negeert dit. Deze sysctl instrueert de kernel om gemarkeerde pakketten te accepteren, wat voorkomt dat een volledige tunnel zichzelf blokkeert.
Start de service en maak het beheerdersaccount aan
cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -fGebruik docker compose up en docker compose down, niet start en stop. start herstart alleen de containers die al bestaan; wijzigingen in het compose-bestand, inclusief netwerk- en sysctl-instellingen, worden daardoor niet toegepast. up maakt daarentegen alles opnieuw aan wat is gewijzigd. Als u wilt dat de stack na een reboot automatisch start, dan voorziet restart: unless-stopped hier al in. Het opstartgedrag van compose-services legt uit wat dit beleid wel en niet garandeert.
De web-UI luistert op TCP 51821. Bij het eerste bezoek verschijnt een configuratiepagina waar u het beheerdersaccount aanmaakt en het hostadres bevestigt dat clients gebruiken om de server te bereiken. Dit hostadres komt in de Endpoint-regel van elke clientconfiguratie terecht; het moet dus het publieke IP-adres of de DNS-naam van de VPS zijn. Als dit onjuist is, verwijst de QR-code die u aan een telefoon geeft naar een onbereikbare locatie en komt de handshake nooit tot stand.
Nog een punt over deze poort: wg-easy 15 weigert standaard HTTP tenzij u INSECURE=true instelt. Het bereiken van de interface via HTTPS met een niet-vertrouwd certificaat, of het afhandelen van TLS bij een reverse proxy voor de service, is beide toegestaan. Het bereiken via http:// met de standaardinstellingen is niet mogelijk.
Stel de UI-poort niet open naar het internet
Het compose-bestand publiceert 51821 op elke interface. Dit is een inlogpagina voor een systeem dat uw verkeer kan routeren en deze mag niet toegankelijk zijn voor de buitenwereld. Het publiceren van een poort in Docker schrijft regels naar de DOCKER-chain, die vóór ufw wordt geëvalueerd. Hierdoor sluit een ufw deny-regel de poort niet af. Deze valkuil is belangrijk om te begrijpen; waarom Docker gepubliceerde poorten ufw negeren behandelt dit volledig.
De eenvoudige oplossing is om de UI aan loopback te binden en deze te benaderen via een SSH-tunnel:
ports:
- "51820:51820/udp"
- "127.0.0.1:51821:51821/tcp"
environment:
- INSECURE=trueVoer vervolgens vanaf uw laptop het volgende uit:
ssh -L 51821:127.0.0.1:51821 youruser@your.server.addressOpen http://127.0.0.1:51821 in de browser op uw laptop. Het verkeer wordt versleuteld door SSH, de poort reageert op niemand anders en INSECURE=true is hier veilig omdat de ongecodeerde HTTP-stap de loopback-interface nooit verlaat.
Open UDP 51820 en controleer beide firewalls
WireGuard zelf vereist dat UDP 51820 bereikbaar is vanaf het internet. Docker publiceert deze poort, maar veel providers plaatsen een externe netwerkfirewall voor de VPS waar Docker geen weet van heeft. Open de poort op beide locaties. Als u de host-firewall beheert met ufw, is de basis ufw-regels voor een VPS een snellere methode dan het handmatig schrijven van nftables.
Controleer of de container daadwerkelijk luistert:
sudo ss -ulnp | grep 51820U hoort een luisterende UDP-socket te zien. Als er niets op die regel staat, betekent dit dat de container de interface niet heeft geactiveerd, en sudo docker compose logs wg-easy zal de reden hiervoor aangeven.
Een client aanmaken en scannen met een telefoon
Maak in de UI een client aan en geef deze een naam die u later herkent, zoals het apparaat waarvoor deze bedoeld is. wg-easy wijst het eerstvolgende vrije tunneladres toe en genereert het sleutelpaar voor u. Elke clientrij biedt een QR-code en een downloadbaar .conf-bestand.
Installeer de officiële WireGuard-app op de telefoon, kies voor het toevoegen van een tunnel via een QR-code en richt de camera op de code op uw scherm. De tunnel verschijnt met de naam die u heeft ingevoerd. Schakel deze in; de clientrij in de UI begint nu overdrachtstellers en de tijd van de meest recente handshake weer te geven. Zodra een telefoon verbonden is met de tunnel, kan deze services bereiken die u nooit op internet heeft gepubliceerd. Zo kan een telefoon overal vandaan foto's uploaden naar een zelfgehoste fotoserver, zonder dat die server ook maar één poort open heeft staan naar de buitenwereld. Dezelfde methode werkt voor media; een Jellyfin-bibliotheek die is ingericht als een videotheek uit de jaren 90 is prettig om vanuit een hotelkamer te doorzoeken, terwijl deze net zo privé blijft als op uw LAN. Waarschuwingen werken via dezelfde tunnel de andere kant op, aangezien een zelfgehoste ntfy-server een bericht naar die telefoon kan pushen zodra een back-uptaak mislukt, zonder ooit een verzoek vanaf het openbare internet te beantwoorden.
Een client die na het inschakelen geen handshake toont, bereikt de server niet. Dit wijst op UDP 51820, hetzij bij de firewall van de provider, hetzij bij het endpoint-adres dat in de configuratie is vastgelegd. Een client die wel een handshake toont maar geen werkende internetverbinding heeft, wijst op problemen met forwarding of DNS.
Download op een desktop het .conf-bestand en importeer dit in de WireGuard-client in plaats van het opnieuw in te typen. De privésleutel in dat bestand wordt eenmalig gegenereerd en getoond. Behandel dit bestand zoals u een SSH-privésleutel behandelt.
Wanneer de UI niet langer volstaat
wg-easy is het juiste hulpmiddel zolang uw peers uit mensen en telefoons bestaan. De UI is sneller dan het bewerken van configuratiebestanden en het intrekken van de toegang voor een verloren telefoon kost slechts één klik.
U bereikt de grenzen van de tool zodra u functionaliteit nodig heeft die niet in de UI is opgenomen. Site-to-site routing, waarbij de AllowedIPs van een peer een volledig extern subnet beslaat in plaats van één enkel adres, is meestal het eerste struikelblok. Split tunnels met routingregels per peer, of een configuratie die door uw provisioning-tool wordt gegenereerd, zijn de volgende stappen. Op dat punt is een handmatige configuratie niet moeilijker, maar simpelweg anders, en de standaard WireGuard-handleiding toont dezelfde tunnel opgebouwd vanuit wg0.conf. Als u liever helemaal stopt met het draaien van het control plane, behandelt WireGuard vergeleken met Tailscale de beheerde optie. Of dit een goede ruil is, hangt af van wat de coördinatieserver daadwerkelijk kan bereiken, en het vertrouwensmodel van Tailscale is het lezen waard voordat u uw netwerk aan hen toevertrouwt. Kosten zijn meestal de volgende vraag, en wat het gratis abonnement van Tailscale daadwerkelijk dekt is voldoende voor een huishouden of een klein team om niets te betalen. Voorbij dat punt telt de facturatie gebruikers in plaats van apparaten; dit is een andere structuur dan de VPS waar u al voor betaalt, dus wat Tailscale kost zodra u het gratis abonnement ontgroeit is het cijfer om te controleren voordat u een team migreert. De volledige tunnel die u zojuist heeft gebouwd heeft daar een direct equivalent, aangezien het adverteren van de VPS als Tailscale exit node u dezelfde route naar buiten via de server oplevert, goedgekeurd in de beheerconsole in plaats van handmatig geschreven in elke clientconfiguratie. De subnet-beperking heeft ook een equivalent, omdat het adverteren van een volledig privénetwerk vanaf de VPS dat netwerk beschikbaar stelt aan elk apparaat in de tailnet zonder de bewerkingen per peer in AllowedIPs die u wegduwden bij de UI. Als u dat dashboard en de automatische mesh-routing wilt, maar niet de coördinatieserver van een externe partij, dan houdt het draaien van uw eigen NetBird-server op een VPS het control plane op hardware die u zelf bezit, ten koste van het DNS- en TLS-beheer waar wg-easy nooit om vroeg.
Als de bovenstaande compose-syntaxis het onbekende onderdeel was in plaats van het WireGuard-gedeelte, legt Docker Compose basisprincipes op een VPS het bestandsformaat en de dagelijkse commando's uit.
FAQ
Waarom negeert wg-easy mijn WG_HOST en PASSWORD_HASH?
Deze variabelen horen bij wg-easy 14. Versie 15 is een herschreven versie, waarbij de upstream-ontwikkelaars bijna alle configuratie hebben verplaatst naar het beheerderspaneel in de web-UI. De container leest beide variabelen niet; hij start normaal op en vraagt u bij het eerste bezoek om een beheerdersaccount aan te maken. Stel het hostadres voor clients in op die configuratiepagina.
Heb ik SYS_MODULE nodig als mijn kernel al over WireGuard beschikt?
Nee. SYS_MODULE en de /lib/modules mount bestaan zodat de container de module kan laden wanneer de host dit niet heeft gedaan. Op een host waar sudo modprobe wireguard al slaagt, wordt de capability niet gebruikt. Het verwijderen ervan is een verstandige stap voor hardening, en NET_ADMIN is in beide gevallen nog steeds vereist.
De client maakt verbinding, maar er is geen internet. Wat is er mis?
Een handshake zonder verkeer duidt bijna altijd op problemen met forwarding. Controleer of net.ipv4.ip_forward=1 en net.ipv4.conf.all.src_valid_mark=1 nog in het compose-bestand staan, aangezien deze bij handmatige aanpassingen vaak verloren gaan. Als forwarding is ingeschakeld, controleer dan de DNS-server die de client heeft ontvangen. Een tunnel die al het verkeer door de VPN stuurt, maar verwijst naar een DNS-server die niet langer bereikbaar is, lijkt in een browser precies op een verbroken verbinding.
Hoe maak ik een back-up van mijn clients?
Alles bevindt zich in het etc_wireguard named volume. In versie 15 betekent dit een SQLite-database op /etc/wireguard/wg-easy.db in de container. De documentatie van v15 beschrijft geen back-up- of exportfunctie, dus maak een back-up van het volume zelf: voer docker compose down uit, kopieer de inhoud van het volume naar een locatie buiten de server en breng de stack vervolgens weer online met docker compose up -d. Doe dit vóór elke upgrade. Het wg0.json-bestand waar oudere handleidingen naar verwijzen, hoort bij wg-easy 14. Dit is wat de v14 back-upknop exporteert, en in v15 is het enige nut ervan het bestand dat u uploadt in de configuratiewizard wanneer u een oude installatie migreert.
Kan ik wg-easy achter een reverse proxy draaien?
Ja. Plaats de proxy voor TCP 51821, beëindig daar TLS en stel INSECURE=true in op de container zodat deze de ongecodeerde HTTP-hop van de proxy accepteert. Houd UDP 51820 direct gepubliceerd, omdat het VPN-verkeer UDP is en niet via een HTTP-proxy verloopt.