SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor

wg-easy mit Docker Compose: WireGuard-Weboberfläche

Richten Sie WireGuard mit wg-easy und Docker Compose ein: Ports, NET_ADMIN, wichtige sysctls und QR-Codes für die schnelle Smartphone-Einrichtung.

Was Sie erstellen

wg-easy ist WireGuard mit einer Weboberfläche und läuft als einzelner Docker-Container. Es verwaltet die WireGuard-Schnittstelle für Sie und stellt eine Browseroberfläche zum Erstellen von Clients bereit. Für jeden erstellten Client werden eine Konfigurationsdatei und ein QR-Code erzeugt. Ein Smartphone tritt dem VPN bei, indem Sie die Kamera auf den Bildschirm richten.

Der Tunnel selbst ist gewöhnliches WireGuard. Das Kernelmodul überträgt die Pakete, daher ist der Durchsatz derselbe wie bei einer manuell erstellten Konfiguration. Sie gewinnen eine einfache Verwaltung des Client-Lebenszyklus: Peers hinzufügen, deaktivieren und löschen, ohne eine Konfigurationsdatei über SSH zu bearbeiten. Sie geben jedoch die direkte Kontrolle über diese Konfiguration auf. Das ist Gegenstand von der manuellen WireGuard-Konfiguration auf einem VPS.

Sie benötigen einen verwaltbaren Linux-Host, beispielsweise einen KVM-VPS, eine öffentliche IP-Adresse oder einen Domainnamen, der auf diesen Host zeigt, eine x86_64- oder arm64-CPU, Docker Engine mit dem Compose-Plugin und Zugriff als root. Container-Virtualisierung, die den Kernel des Hosts gemeinsam nutzt, beispielsweise OpenVZ oder LXC, kann das WireGuard-Modul normalerweise nicht laden. Der Container kann die Schnittstelle dann nicht starten.

Version 15 hat die Einstellungen aus der Umgebung verschoben

Die meisten Anleitungen, die Sie finden, wurden für wg-easy 14 geschrieben. Dort setzen Sie WG_HOST auf die Adresse Ihres Servers und PASSWORD_HASH auf einen bcrypt-Hash des Admin-Passworts. Beides wird als Umgebungsvariable festgelegt. Version 15 ist eine Neuentwicklung. In den offiziellen Migrationshinweisen steht eindeutig, dass v15 nicht dieselben Umgebungsvariablen wie v14 verwendet und dass die meisten Einstellungen in das Admin-Panel der Weboberfläche verschoben wurden.

WG_HOST und PASSWORD_HASH haben daher keine Wirkung mehr. Wenn Sie eine alte Compose-Datei kopieren, startet der Container, ignoriert diese Zeilen und fordert Sie anschließend im Browser auf, ein Admin-Konto anzulegen. Das ist kein Fehler. Dies ist der neue Einrichtungsablauf.

Stand Oktober 2026 ist 15 der wichtige Tag, den Sie fest pinnen sollten. Verwenden Sie die Hauptversion statt latest, weil ein Upgrade auf eine neue Hauptversion das Format der Konfiguration auf dem Datenträger ändert und kein sauberes Rollback ermöglicht. Mit dem Wechsel auf 15 wurde die JSON-Datei aus v14 durch eine SQLite-Datenbank ersetzt.

Die Compose-Datei

Erstellen Sie ein Verzeichnis für den Stack und schreiben Sie die offizielle Compose-Datei hinein. Dies ist die Datei des Upstream-Projekts, wobei die auskommentierten optionalen Zeilen entfernt wurden.

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.yml

Der Inhalt sieht so aus:

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::/64

etc_wireguard ist ein benanntes Volume. Es enthält den Serversschlüssel und jeden von Ihnen erstellten Client. Die Daten werden in der SQLite-Datenbank wg-easy.db gespeichert. Sichern Sie dieses Volume. Andernfalls gehen bei einem erneuten Build alle Ihre Peers verloren. Wenn Sie diese Dateien lieber im Host-Dateisystem sehen möchten, ersetzen Sie das Volume durch einen Bind-Mount. Lesen Sie vorher den Unterschied zwischen Bind-Mounts und benannten Volumes, da sich die Berechtigungen unterschiedlich verhalten.

Warum NET_ADMIN, SYS_MODULE und die sysctls benötigt werden

Ein Container darf standardmäßig nicht auf den Netzwerk-Stack zugreifen. Jede dieser Zeilen hebt eine bestimmte Einschränkung auf.

NET_ADMIN ermöglicht dem Container, die wg0-Schnittstelle zu erstellen, ihr eine Adresse zuzuweisen und Routen zu schreiben. Ohne diese Berechtigung startet der Container und beendet sich anschließend beim Aktivieren der Schnittstelle, weil ip link add wg0 type wireguard den Wert Operation not permitted zurückgibt.

SYS_MODULE und der schreibgeschützte Mount von /lib/modules ermöglichen es dem Container, das WireGuard-Kernelmodul zu laden, falls der Host es noch nicht geladen hat. Das Modul befindet sich im Host-Kernel, nicht im Image. Deshalb muss das Host-Verzeichnis im Container sichtbar sein. In einem modernen Kernel ist das Modul normalerweise fest integriert. Mit sudo modprobe wireguard && echo ok auf dem Host können Sie das überprüfen.

net.ipv4.ip_forward=1 weist den Kernel an, Pakete weiterzuleiten, die nicht an den Host selbst adressiert sind. Ohne diese Einstellung verbindet sich ein Client, der Handshake ist erfolgreich, und anschließend wird jedes Paket ins Internet verworfen. Daher läuft ping 1.1.1.1 ab, obwohl das VPN als verbunden angezeigt wird.

net.ipv4.conf.all.src_valid_mark=1 sorgt häufig für Verwirrung. WireGuard markiert die eigenen ausgehenden Pakete, damit sie nicht zurück in den Tunnel geroutet werden. Striktes Reverse Path Filtering erkennt ein Paket, dessen Quelladresse nicht zur erwarteten Route passt, und verwirft es. Dieses sysctl weist den Kernel an, markierte Pakete zu akzeptieren. Dadurch verhindert es, dass ein Full Tunnel seine eigene Verbindung unterbricht.

Starten und das Administratorkonto erstellen

cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -f

Verwenden Sie docker compose up und docker compose down, nicht start und stop. start startet nur die bereits vorhandenen Container neu. Änderungen an der Compose-Datei, einschließlich der Netzwerk- und sysctl-Einstellungen, werden dadurch nicht angewendet. up erstellt dagegen alle geänderten Komponenten neu. Wenn der Stack nach einem Reboot wieder gestartet werden soll, ist restart: unless-stopped dafür bereits zuständig. Das Startverhalten von Compose-Diensten erklärt, was diese Richtlinie leistet und was sie nicht garantiert.

Die Weboberfläche lauscht auf TCP 51821. Beim ersten Aufruf wird eine Einrichtungsseite angezeigt. Dort erstellen Sie das Administratorkonto und bestätigen die Hostadresse, über die Clients den Server erreichen. Diese Hostadresse wird in jeder Client-Konfiguration in der Endpoint-Zeile eingetragen. Sie muss daher die öffentliche IP-Adresse oder der DNS-Name des VPS sein. Ist sie falsch, verweist der QR-Code, den Sie an ein Telefon weitergeben, auf ein nicht erreichbares Ziel. Der Handshake wird dann nicht abgeschlossen.

Noch ein Hinweis zu diesem Port: wg-easy 15 lehnt unverschlüsseltes HTTP ab, sofern Sie nicht INSECURE=true setzen. Der Zugriff über HTTPS mit einem nicht vertrauenswürdigen Zertifikat ist möglich. Auch eine TLS-Terminierung an einem vorgeschalteten Reverse Proxy ist möglich. Der Zugriff über http:// mit den Standardeinstellungen funktioniert dagegen nicht.

Veröffentlichen Sie den UI-Port nicht im Internet

Die Compose-Datei veröffentlicht 51821 auf allen Schnittstellen. Das ist eine Anmeldeseite für ein System, das Ihren Netzwerkverkehr weiterleiten kann. Sie sollte nicht weltweit erreichbar sein. Das Veröffentlichen eines Ports in Docker schreibt Regeln in die Kette DOCKER. Diese wird vor ufw ausgewertet. Daher schließt eine ufw-deny-Regel den Port nicht. Diese Falle sollten Sie separat verstehen. Warum veröffentlichte Docker-Ports ufw ignorieren erklärt sie vollständig.

Die einfache Lösung besteht darin, die UI an loopback zu binden und sie über einen SSH-Tunnel zu erreichen:

    ports:
      - "51820:51820/udp"
      - "127.0.0.1:51821:51821/tcp"
    environment:
      - INSECURE=true

Führen Sie anschließend auf Ihrem Laptop Folgendes aus:

ssh -L 51821:127.0.0.1:51821 youruser@your.server.address

Öffnen Sie http://127.0.0.1:51821 im Browser auf Ihrem Laptop. SSH verschlüsselt den Datenverkehr. Der Port antwortet keinem anderen System. INSECURE=true ist hier sicher, weil der unverschlüsselte HTTP-Abschnitt die loopback-Schnittstelle nicht verlässt.

UDP-Port 51820 öffnen und beide Firewalls prüfen

WireGuard muss über UDP-Port 51820 aus dem Internet erreichbar sein. Docker veröffentlicht den Port, aber viele Anbieter schalten eine separate Netzwerk-Firewall vor die VPS, die Docker nicht kennt. Öffnen Sie den Port an beiden Stellen. Wenn Sie die Host-Firewall mit ufw verwalten, sind die grundlegenden ufw-Regeln für eine VPS der kürzere Weg, als nftables manuell zu konfigurieren.

Prüfen Sie, ob der Container tatsächlich auf dem Port lauscht:

sudo ss -ulnp | grep 51820

Sie sollten einen lauschenden UDP-Socket sehen. Wenn in dieser Zeile nichts angezeigt wird, hat der Container die Schnittstelle nicht aktiviert. sudo docker compose logs wg-easy nennt den Grund.

Einen Client erstellen und auf einem Telefon scannen

Erstellen Sie in der Benutzeroberfläche einen Client und geben Sie ihm einen Namen, den Sie später wiedererkennen, zum Beispiel den Namen des Geräts, zu dem er gehört. wg-easy weist die nächste freie Tunneladresse zu und generiert das Schlüsselpaar für Sie. Jede Clientzeile bietet einen QR-Code und eine herunterladbare .conf-Datei.

Installieren Sie die offizielle WireGuard-App auf dem Telefon. Wählen Sie das Hinzufügen eines Tunnels über einen QR-Code und richten Sie die Kamera auf den Code auf Ihrem Bildschirm. Der Tunnel wird mit dem eingegebenen Namen angezeigt. Aktivieren Sie ihn. In der Clientzeile der Benutzeroberfläche erscheinen anschließend Übertragungszähler und ein aktueller Handshake-Zeitpunkt. Sobald ein Telefon den Tunnel verwendet, kann es Dienste erreichen, die Sie nie im Internet veröffentlicht haben. So kann ein Telefon von überall weiter Bilder auf einen selbst gehosteten Fotoserver hochladen, ohne dass dieser Server auch nur einen Port für die Öffentlichkeit öffnen muss. Dasselbe gilt für Medien. Eine Jellyfin-Bibliothek, die wie eine Videothek aus den 90er-Jahren aufgebaut ist, lässt sich angenehm vom Hotelzimmer aus durchsuchen, während sie genauso privat bleibt wie in Ihrem LAN. Benachrichtigungen funktionieren über denselben Tunnel auch in die andere Richtung. Ein selbst gehosteter ntfy-Server kann dem Telefon sofort eine Nachricht senden, wenn ein Backup-Job fehlschlägt, ohne jemals eine Anfrage aus dem öffentlichen Internet zu beantworten.

Ein Client, bei dem nach der Aktivierung kein Handshake angezeigt wird, erreicht den Server überhaupt nicht. Das deutet auf UDP 51820 hin, entweder auf der Firewall des Providers oder bei der in der Konfiguration hinterlegten Endpoint-Adresse. Ein Client, bei dem ein Handshake angezeigt wird, aber kein funktionierender Internetzugang besteht, deutet dagegen auf die Weiterleitung oder auf DNS hin.

Laden Sie auf einem Desktop die .conf-Datei herunter und importieren Sie sie in den WireGuard-Client, anstatt sie erneut einzugeben. Der private Schlüssel in dieser Datei wird einmal generiert und einmal angezeigt. Behandeln Sie die Datei wie einen privaten SSH-Schlüssel.

Wann die Weboberfläche nicht mehr ausreicht

wg-easy ist das passende Werkzeug, solange Ihre Peers Personen und Smartphones sind. Die Weboberfläche ist schneller als das Bearbeiten von Konfigurationsdateien. Ein verlorenes Smartphone lässt sich mit einem Klick sperren.

Die Grenzen erreichen Sie, sobald Sie etwas benötigen, das die Weboberfläche nicht abbildet. Site-to-Site-Routing ist meist die erste Hürde. Dabei umfasst AllowedIPs eines Peers ein ganzes entferntes Subnetz statt nur einer einzelnen Adresse. Danach folgen Split-Tunnel mit Peer-spezifischen Routing-Regeln oder eine von Ihrem Provisioning-Tool erzeugte Konfiguration. Dann ist die manuelle Konfiguration nicht schwieriger, sondern lediglich anders. Die Anleitung für reines WireGuard zeigt denselben Tunnel, der aus wg0.conf erstellt wird. Wenn Sie die Control Plane überhaupt nicht mehr betreiben möchten, behandelt WireGuard im Vergleich mit Tailscale die verwaltete Option. Ob sich dieser Tausch lohnt, hängt davon ab, welche Systeme der Koordinationsserver tatsächlich erreichen kann. Lesen Sie daher das Vertrauensmodell von Tailscale, bevor Sie ihm Zugriff auf Ihr Netzwerk geben. Die Kosten sind meist die nächste Frage. Der tatsächliche Umfang des kostenlosen Tailscale-Tarifs umfasst bis zu sechs Benutzer kostenlos und ausschließlich für die nichtkommerzielle Nutzung. Ein Haushalt zahlt daher nichts, während ein kleines Unternehmen unabhängig von der Anzahl seiner Mitarbeiter nicht qualifiziert ist. Danach werden Benutzer statt Geräte abgerechnet. Das unterscheidet die Kostenstruktur von einem VPS, den Sie bereits bezahlen. Prüfen Sie daher die Kosten von Tailscale nach dem kostenlosen Tarif, bevor Sie ein Team migrieren. Der gerade erstellte vollständige Tunnel hat dort ein direktes Gegenstück. Das Bewerben des VPS als Tailscale-Exit-Node stellt dieselbe Route über den Server bereit. Sie wird jedoch in der Administrationskonsole genehmigt und nicht in jede Client-Konfiguration geschrieben. Auch für die Subnetz-Hürde gibt es ein Gegenstück. Das Bewerben eines ganzen privaten Netzwerks vom VPS stellt dieses Netzwerk jedem Gerät im Tailnet bereit, ohne die Peer-spezifische Bearbeitung von AllowedIPs, die Sie von der Weboberfläche weggeführt hat. Wenn Sie dieses Dashboard und das automatische Mesh-Routing möchten, aber keinen Koordinationsserver eines anderen Betreibers, hält der Betrieb eines eigenen NetBird-Servers auf einem VPS die Control Plane auf Ihrer eigenen Hardware. Dafür müssen Sie DNS und TLS einrichten, was bei wg-easy nicht erforderlich war.

Wenn die Compose-Syntax oben der ungewohnte Teil war und nicht WireGuard, erklärt Docker-Compose-Grundlagen auf einem VPS das Dateiformat und die alltäglichen Befehle.

FAQ

Warum ignoriert wg-easy meine Variablen WG_HOST und PASSWORD_HASH?

Diese Variablen gehören zu wg-easy 14. Version 15 wurde neu geschrieben, und das Upstream-Projekt hat fast die gesamte Konfiguration in das Admin-Panel der Weboberfläche verschoben. Der Container liest keine der beiden Variablen ein. Er startet daher normal und fordert Sie beim ersten Aufruf auf, ein Administratorkonto anzulegen. Legen Sie die clientseitig sichtbare Hostadresse stattdessen auf dieser Einrichtungsseite fest.

Benötige ich SYS_MODULE, wenn mein Kernel WireGuard bereits enthält?

Nein. Der Eintrag SYS_MODULE und der Mount /lib/modules ermöglichen es dem Container, das Modul zu laden, wenn es auf dem Host nicht vorhanden ist. Auf einem Host, auf dem sudo modprobe wireguard bereits erfolgreich ausgeführt wird, bleibt diese Capability ungenutzt. Das Entfernen ist eine sinnvolle Hardening-Maßnahme. NET_ADMIN wird unabhängig davon weiterhin benötigt.

Der Client stellt eine Verbindung her, aber es gibt kein Internet. Was ist falsch?

Ein Handshake ohne Datenverkehr weist fast immer auf fehlendes Forwarding hin. Prüfen Sie, ob net.ipv4.ip_forward=1 und net.ipv4.conf.all.src_valid_mark=1 noch in der Compose-Datei enthalten sind. Eine manuell bearbeitete Kopie verliert diese Einträge häufig. Wenn das Forwarding aktiviert ist, prüfen Sie den DNS-Server, den der Client erhalten hat. Ein Tunnel, der den gesamten Datenverkehr durch das VPN leitet, aber auf einen DNS-Server verweist, den er nicht mehr erreichen kann, sieht im Browser genau wie eine unterbrochene Verbindung aus.

Wie sichere ich meine Clients?

Alle Daten befinden sich im benannten Volume etc_wireguard. In Version 15 handelt es sich dabei um eine SQLite-Datenbank unter /etc/wireguard/wg-easy.db im Container. Die v15-Dokumentation beschreibt keine Backup- oder Exportfunktion. Sichern Sie daher das Volume selbst: Führen Sie docker compose down aus, kopieren Sie den Inhalt des Volumes an einen Ort außerhalb des Servers und starten Sie den Stack anschließend mit docker compose up -d wieder. Führen Sie dies vor jedem Upgrade aus. Die Datei wg0.json, die in älteren Anleitungen erwähnt wird, gehört zu wg-easy 14. Sie ist die Datei, die der Backup-Button in v14 exportiert. In v15 wird sie nur noch als Datei verwendet, die Sie im Einrichtungsassistenten hochladen, wenn Sie eine alte Installation migrieren.

Kann ich wg-easy hinter einem Reverse Proxy betreiben?

Ja. Platzieren Sie den Proxy vor TCP 51821, terminieren Sie TLS dort und setzen Sie INSECURE=true am Container, damit dieser den unverschlüsselten HTTP-Hop des Proxys akzeptiert. Veröffentlichen Sie UDP 51820 weiterhin direkt, da der VPN-Datenverkehr UDP verwendet und keinen HTTP-Proxy durchläuft.