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 Sie den Dienst und erstellen Sie das Administratorkonto

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 dabei nicht angewendet. up erstellt dagegen alle geänderten Komponenten neu. Wenn der Stack nach einem Reboot wieder gestartet werden soll, deckt restart: unless-stopped diesen Fall bereits ab. 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, unter der die Clients den Server erreichen. Diese Hostadresse wird in jeder Client-Konfiguration in die Zeile Endpoint übernommen. Daher muss dort die öffentliche IP-Adresse oder der DNS-Name des VPS eingetragen werden. Ist die Adresse falsch, verweist der an ein Telefon übergebene QR-Code auf ein nicht erreichbares Ziel. Der Handshake wird dann nicht abgeschlossen.

Noch ein Hinweis zu diesem Port: wg-easy stellt selbst kein TLS bereit und weist unverschlüsseltes HTTP nicht zurück. Über http:// wird die Oberfläche weiterhin geladen und ein Warnhinweis angezeigt. Standardmäßig wird jedoch das Session-Cookie geändert. Es enthält das Secure-Flag. Ruft ein Browser die Oberfläche über unverschlüsseltes HTTP unter einer öffentlichen Adresse auf, verwirft er das Cookie, und die Anmeldung bleibt nicht bestehen. INSECURE=true entfernt dieses Flag und führt keine weiteren Änderungen durch. Wenn ein Reverse Proxy TLS terminiert, ist an wg-easy keine Änderung erforderlich, auch wenn ein selbstsigniertes Zertifikat verwendet wird.

Veröffentlichen Sie den UI-Port nicht im Internet

Die Compose-Datei veröffentlicht Port 51821 auf allen Schnittstellen. Dahinter befindet sich eine Anmeldeseite für einen Host, der Ihren Netzwerkverkehr weiterleiten kann. Der Port sollte daher nicht für alle zugänglich sein. Durch die Veröffentlichung eines Ports in Docker werden Regeln in die Kette DOCKER geschrieben. Diese wird vor ufw ausgewertet. Eine ufw-deny-Regel schließt den Port daher nicht. Dieser Fall ist grundsätzlich wichtig. Warum von Docker veröffentlichte Ports ufw ignorieren behandelt ihn ausführlich.

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

    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. Der Datenverkehr wird durch SSH verschlüsselt, und der Port beantwortet keine Anfragen anderer Hosts. INSECURE=true entfernt lediglich das Secure-Flag aus dem Session-Cookie. Dadurch behält der Browser Ihre Anmeldung unter dieser unverschlüsselten HTTP-Adresse bei. Das 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. Upstream hat fast die gesamte Konfiguration in das Administrationsfeld der Weboberfläche verschoben. Der Container liest keine der beiden Variablen. Er startet normal und fordert Sie beim ersten Aufruf auf, ein Administratorkonto zu erstellen. Tragen Sie die für die Clients erreichbare Hostadresse stattdessen auf dieser Einrichtungsseite ein.

Benötige ich SYS_MODULE, wenn mein Kernel WireGuard bereits unterstützt?

Nein. SYS_MODULE und der Mount /lib/modules sind vorhanden, damit der Container das Modul laden kann, wenn der Host es noch nicht geladen hat. Auf einem Host, auf dem sudo modprobe wireguard bereits erfolgreich ausgeführt wird, wird diese Berechtigung nicht verwendet. Das Entfernen ist eine sinnvolle Härtungsmaß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 ein Problem mit der Weiterleitung hin. Prüfen Sie, ob net.ipv4.ip_forward=1 und net.ipv4.conf.all.src_valid_mark=1 weiterhin in der Compose-Datei enthalten sind. Eine manuell bearbeitete Kopie verliert diese Einstellungen häufig. Wenn die Weiterleitung 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, wirkt im Browser genauso wie eine unterbrochene Verbindung.

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 Dokumentation für v15 beschreibt keine Sicherungs- 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 durch. Die Datei wg0.json, die in älteren Anleitungen erwähnt wird, gehört zu wg-easy 14. Über die Sicherungsschaltfläche von v14 wird diese Datei 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. Setzen Sie den Proxy vor den Port der Weboberfläche, lassen Sie ihn TLS terminieren und lassen Sie INSECURE nicht gesetzt. Der Container akzeptiert den unverschlüsselten HTTP-Abschnitt vom Proxy unverändert. Die offiziellen Beispiele für Caddy und Traefik setzen überhaupt keine Variable INSECURE. Da der Browser über HTTPS mit dem Proxy kommuniziert, soll das Session-Cookie sein Secure-Flag behalten. Dieses Flag wird ausschließlich von INSECURE=true entfernt. Lassen Sie UDP 51820 direkt veröffentlicht, weil der VPN-Datenverkehr UDP verwendet und nicht über einen HTTP-Proxy läuft.

War dieser Artikel hilfreich?