wg-easy v15: Migration und neues Setup
Mit Version 15 hat wg-easy einen kompletten Rewrite bekommen. Die Konfiguration wandert von Environment-Variablen in ein Admin Panel mit SQLite-Datenbank, es gibt einen Setup-Wizard, und die docker-compose.yml schrumpft auf ein Minimum zusammen. Klingt gut - aber was bedeutet das für bestehende Installationen?
Falls du WireGuard und wg-easy noch nicht kennst: Im WireGuard-Grundlagen-Post findest du alles zu Konzepten, Client-Einrichtung und Netzwerk-Konfiguration.
Was ist neu in v15?
Die wichtigste Änderung: Fast alles, was du bisher in der docker-compose.yml konfiguriert hast, passiert jetzt über das Web-Interface.
| Aspekt | v14 | v15 |
|---|---|---|
| Konfiguration | Environment-Variablen in docker-compose.yml |
Admin Panel im Web-UI |
| Passwort | PASSWORD_HASH (bcrypt) |
Username + Passwort via Setup Wizard |
| DNS | WG_DEFAULT_DNS Env-Var |
Admin Panel > WireGuard > DNS Servers |
| Allowed IPs | WG_ALLOWED_IPS Env-Var |
Admin Panel > WireGuard > Allowed IPs |
| PostUp/PostDown | WG_POST_UP, WG_POST_DOWN Env-Vars |
Admin Panel > Hooks |
| Persistenz | JSON-Dateien im Volume | SQLite-Datenbank |
| Ersteinrichtung | Container starten, fertig | Setup Wizard im Browser |
| Env-Vars | ~12 Variablen | 3 Variablen (Port, Host, Path) |
Die bestehenden Clients funktionieren nach der Migration weiter - du musst auf den Endgeräten nichts ändern, solange du die
wg0.jsonimportierst.
Frische Installation
Wenn du noch kein wg-easy hast, ist v15 der perfekte Einstieg. Die docker-compose.yml ist erfrischend kurz:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
hostname: wg-easy
environment:
- PORT=51821
- WG_HOST=vpn.example.com
- WG_PATH=/
volumes:
- ./data:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
restart: unless-stopped
Das war’s. Drei Environment-Variablen:
PORT- Der Port für das Web-Interface (Standard: 51821)WG_HOST- Deine öffentliche Domain oder IPWG_PATH- Der Pfad für das Web-UI (nützlich bei Reverse Proxies)
1
docker compose up -d
Beim ersten Aufruf von http://dein-server:51821 begrüßt dich der Setup Wizard. Dort legst du einen Admin-Account mit Username und Passwort an - kein bcrypt-Hash mehr, kein Generieren in der Shell.
WG_HOSTmuss die Adresse sein, unter der dein WireGuard-Server von außen erreichbar ist. Bei einer Cloud-VM ist das die öffentliche IP, hinter einem Router die DynDNS-Domain.
Migration von v14 auf v15
Die Migration ist nicht kompliziert, erfordert aber ein paar Schritte in der richtigen Reihenfolge.
Schritt 1: Backup erstellen
Bevor du irgendetwas änderst - sichere deinen aktuellen Stand:
1
2
3
4
5
# docker-compose.yml sichern
cp docker-compose.yml docker-compose.yml.v14.bak
# WireGuard-Konfiguration sichern
cp -r ./config ./config.v14.bak
Zusätzlich die Client-Konfigurationen als wg0.json exportieren. In wg-easy v14 öffnest du das Web-UI und lädst die Konfiguration herunter, oder du kopierst die JSON-Datei direkt aus dem Volume:
1
cp ./config/wg0.json ./wg0.json.bak
Ohne die
wg0.jsonverlierst du alle Client-Konfigurationen. Diesen Schritt auf keinen Fall überspringen!
Schritt 2: Container stoppen
1
docker compose down
Schritt 3: docker-compose.yml umschreiben
Ersetze deine v14-Konfiguration mit der neuen v15-Version.
Vorher (v14):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:14
container_name: wg-easy
environment:
- WG_HOST=vpn.example.com
- PASSWORD_HASH=$$2a$$12$$abc123...
- WG_PORT=51820
- WG_DEFAULT_ADDRESS=10.8.0.x
- WG_DEFAULT_DNS=1.1.1.1, 9.9.9.9
- WG_ALLOWED_IPS=0.0.0.0/0
- WG_PERSISTENT_KEEPALIVE=25
- WG_POST_UP=iptables -t nat -A POSTROUTING ...
- WG_POST_DOWN=iptables -t nat -D POSTROUTING ...
- LANG=de
volumes:
- ./config:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
restart: unless-stopped
Nachher (v15):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
hostname: wg-easy
environment:
- PORT=51821
- WG_HOST=vpn.example.com
- WG_PATH=/
volumes:
- ./data:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
restart: unless-stopped
Beachte den neuen Volume-Pfad:
./datastatt./config. Wenn du den gleichen Pfad behalten willst, passe ihn entsprechend an. v15 erstellt eine neue SQLite-Datenbank, die alten JSON-Dateien werden nicht automatisch übernommen.
Schritt 4: Container starten
1
docker compose up -d
Schritt 5: Setup Wizard durchlaufen
Öffne http://dein-server:51821 im Browser. Der Setup Wizard erscheint und fragt nach:
- Admin-Account - Username und Passwort festlegen
- Client-Import - Hier die
wg0.jsonhochladen
Nach dem Import sollten alle deine bestehenden Clients wieder sichtbar sein.
Schritt 6: Admin Panel konfigurieren
Nach dem Import musst du die Einstellungen prüfen und nachjustieren. v15 übernimmt nicht alle Konfigurationen aus der wg0.json:
- DNS Servers - Im Admin Panel unter WireGuard > DNS Servers eintragen (z.B.
1.1.1.1, 9.9.9.9) - Allowed IPs - Unter WireGuard > Allowed IPs setzen (z.B.
0.0.0.0/0für Full Tunnel) - Persistent Keepalive - Unter WireGuard > Persistent Keepalive
- Hooks - PostUp/PostDown Regeln unter Admin > Hooks eintragen (siehe Abschnitt unten)
Nach dem Import sind die Hooks (PostUp/PostDown) leer und DNS steht auf dem Default. Beides manuell im Admin Panel nachtragen!
Stolperfalle: IPv6 auf Cloud-VMs
Wenn dein wg-easy auf einer Cloud-VM (Hetzner, Netcup, etc.) läuft und du beim Start folgende Fehlermeldung siehst:
1
2
ip6tables v1.8.10: can't initialize ip6tables table `filter':
Table does not exist (do you need to insmod?)
Dann fehlen die IPv6-Kernel-Module. Das ist bei vielen Cloud-Providern standardmäßig der Fall.
Lösung:
1
2
3
4
# Module laden
sudo modprobe ip6_tables
sudo modprobe ip6table_filter
sudo modprobe ip6table_nat
Persistent machen (überlebt Reboots):
1
echo -e "ip6_tables\nip6table_filter\nip6table_nat" | sudo tee /etc/modules-load.d/ip6tables.conf
Danach den Container neu starten:
1
docker compose restart wg-easy
Dieses Problem tritt nur auf Cloud-VMs auf, die mit einem minimalen Kernel ausgeliefert werden. Auf einer Proxmox-VM oder einem dedizierten Server hast du die Module in der Regel bereits geladen.
Hooks konfigurieren
In v14 hast du PostUp/PostDown als Environment-Variablen gesetzt. In v15 passiert das im Admin Panel unter Hooks.
Brauche ich MASQUERADE?
Kurze Antwort: Ja, in den meisten Fällen.
MASQUERADE (NAT) schreibt die Quell-IP der VPN-Clients auf die IP der wg-easy-VM um. Ohne MASQUERADE behalten die Pakete ihre WireGuard-IP (z.B. 10.10.10.3) - aber die Zieldienste müssen dann wissen, wie sie Antworten an dieses Subnetz zurückschicken. Dafür braucht jede Ziel-VM (oder das Gateway) eine statische Route für 10.10.10.0/24 über die wg-easy-VM.
Besonders wenn deine wg-easy-VM in einem anderen Subnetz als die Dienste sitzt (z.B. VPN in 192.168.45.0/24, Dienste in 192.168.60.0/24), funktioniert es ohne MASQUERADE und ohne statische Routen schlicht nicht - auch DNS-Anfragen bleiben unbeantwortet, und Clients können keine Hostnamen mehr auflösen.
Ohne MASQUERADE siehst du in den Logs der Zieldienste die einzelnen Client-IPs statt der VM-IP. Falls du diese Transparenz brauchst, richte statische Routen für
10.10.10.0/24auf deinem Gateway oder den Ziel-VMs ein und lass MASQUERADE weg.
Split Tunnel (nur bestimmte Subnetze)
Bei einem Split Tunnel schickst du nur bestimmte Netze über den VPN:
Post Up:
1
iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
Post Down:
1
iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
Full Tunnel
Für einen Full Tunnel, bei dem der gesamte Traffic über den VPN läuft:
Post Up:
1
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
Post Down:
1
iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
Whitelist-Firewall: Clients gezielt einschränken
In einem Homelab willst du externen Nutzern (Familie, Freunde) oft nur bestimmte Dienste freigeben, nicht das gesamte Netzwerk. Mit einer Whitelist-Firewall in den globalen Hooks erreichst du genau das:
Post Up:
1
iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables-nft -I FORWARD 1 -o wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT; iptables-nft -I FORWARD 2 -i wg0 -d 192.168.60.65 -p tcp --dport 443 -j ACCEPT; iptables-nft -I FORWARD 3 -i wg0 -d 192.168.60.141 -p tcp --dport 32400 -j ACCEPT; iptables-nft -I FORWARD 4 -i wg0 -d 192.168.60.141 -p tcp --dport 8096 -j ACCEPT; iptables-nft -I FORWARD 5 -i wg0 -d 192.168.60.141 -p tcp --dport 5055 -j ACCEPT; iptables-nft -I FORWARD 6 -i wg0 -d 192.168.60.101 -p udp --dport 53 -j ACCEPT; iptables-nft -I FORWARD 7 -i wg0 -d 192.168.60.101 -p tcp --dport 53 -j ACCEPT; iptables-nft -I FORWARD 8 -i wg0 -d 192.168.60.102 -p udp --dport 53 -j ACCEPT; iptables-nft -I FORWARD 9 -i wg0 -d 192.168.60.102 -p tcp --dport 53 -j ACCEPT; iptables-nft -I FORWARD 10 -i wg0 -j DROP
Post Down:
1
iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables-nft -D FORWARD -o wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.65 -p tcp --dport 443 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.141 -p tcp --dport 32400 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.141 -p tcp --dport 8096 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.141 -p tcp --dport 5055 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.101 -p udp --dport 53 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.101 -p tcp --dport 53 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.102 -p udp --dport 53 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -d 192.168.60.102 -p tcp --dport 53 -j ACCEPT; iptables-nft -D FORWARD -i wg0 -j DROP
Die Logik dahinter:
| Regel | Zweck |
|---|---|
ESTABLISHED,RELATED |
Antwort-Traffic durchlassen |
.65:443 |
Nginx Proxy Manager (HTTPS) |
.141:32400 |
Plex |
.141:8096 |
Jellyfin |
.141:5055 |
Overseerr |
.101:53, .102:53 |
DNS-Server (UDP + TCP) |
DROP |
Alles andere blockieren |
Die Template-Variablen
{{ipv4Cidr}},{{device}}und{{port}}werden von wg-easy v15 automatisch ersetzt. Du trägst sie genau so in die Hooks ein.
Client-spezifische Hooks: Admin-Zugang ohne Einschränkungen
Die Whitelist-Firewall oben schränkt alle Clients ein - auch einen Admin-Account, den du für die Verwaltung des Homelabs nutzt. Da v15 Hooks pro Client erlaubt, kannst du für den Admin-Client eigene Hooks setzen, die den globalen DROP umgehen.
Gehe dazu im Admin Panel auf den jeweiligen Client und trage dort folgende Hooks ein:
Post Up:
1
iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
Post Down:
1
iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
Das sind MASQUERADE und die Basis-Regeln für Forwarding - ohne die iptables-nft Whitelist und den abschließenden DROP. Damit hat der Admin-Client vollen Zugriff auf alle IPs und Ports im Homelab.
Falls die globalen Hooks zusätzlich zu den Client-Hooks ausgeführt werden, greift der globale DROP trotzdem. In dem Fall musst du im Admin-Client eine zusätzliche ACCEPT-Regel mit niedrigerer Nummer als der DROP einfügen:
iptables-nft -I FORWARD 1 -i wg0 -s <admin-client-ip> -j ACCEPT
Nach dem Einrichten solltest du testen, ob der Admin-Client tatsächlich überall hinkommt - z.B. per ping oder ssh auf eine IP, die in der Whitelist nicht enthalten ist (etwa ein Proxmox-Node).
Rollback auf v14
Falls etwas schiefgeht, kannst du jederzeit auf v14 zurück:
1
2
3
4
5
6
7
8
9
10
# v15 stoppen
docker compose down
# Backup wiederherstellen
cp docker-compose.yml.v14.bak docker-compose.yml
rm -rf ./data
cp -r ./config.v14.bak ./config
# v14 starten
docker compose up -d
Deine Clients verbinden sich wieder wie gewohnt - an der WireGuard-Konfiguration auf den Endgeräten ändert sich beim Rollback nichts.
Troubleshooting
Container startet nicht: ip6tables-Fehler
Siehe Stolperfalle: IPv6 auf Cloud-VMs.
Clients nach Import nicht erreichbar
Prüfe im Admin Panel:
- Sind die Hooks (PostUp/PostDown) korrekt eingetragen?
- Stimmt das Netzwerk-Interface in den iptables-Regeln?
- Ist IP Forwarding auf dem Host aktiv (
sysctl net.ipv4.ip_forward)?
DNS funktioniert nicht nach Migration
v15 setzt DNS auf den Default zurück. Im Admin Panel unter WireGuard > DNS Servers deine gewünschten DNS-Server eintragen.
Änderungen an DNS und Allowed IPs im Admin Panel gelten nur für neue Client-Konfigurationen. Bestehende Clients müssen ihre Konfiguration neu herunterladen, damit die Änderungen wirksam werden.
Web-UI zeigt “Setup Wizard” obwohl bereits konfiguriert
Das passiert, wenn das Volume nicht korrekt gemountet ist oder die SQLite-Datenbank fehlt. Prüfe:
1
ls -la ./data/
Es sollte mindestens eine db.sqlite-Datei vorhanden sein.
Hooks werden nicht ausgeführt
Nach dem Import aus der wg0.json sind die Hooks leer - das ist erwartetes Verhalten. v15 importiert nur die Client-Konfigurationen, nicht die Server-Einstellungen. Hooks manuell im Admin Panel unter Hooks nachtragen.
Fazit
wg-easy v15 ist ein großer Schritt nach vorn. Die Konfiguration über das Admin Panel ist deutlich komfortabler als das Jonglieren mit Environment-Variablen und bcrypt-Hashes. Die Migration selbst ist überschaubar - der wichtigste Punkt ist das Backup der wg0.json und das manuelle Nachtragen von DNS, Allowed IPs und Hooks nach dem Import.
Für neue Installationen gibt es keinen Grund mehr, bei v14 zu bleiben. Für bestehende Setups lohnt sich die Migration, sobald du 15 Minuten Zeit und ein aktuelles Backup hast.