SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-28

wg-easy mit WireGuard in Docker einrichten

So betreiben Sie WireGuard mit wg-easy und Docker Compose: Ports, NET_ADMIN, wichtige sysctls und QR-Code-Anmeldung für Smartphones im Überblick.

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 kann dem VPN beitreten, indem Sie die Kamera auf den Bildschirm richten.

Der Tunnel selbst verwendet gewöhnliches WireGuard. Das Kernelmodul verarbeitet die Pakete. Der Durchsatz entspricht daher dem einer manuell erstellten Konfiguration. Der Vorteil liegt im Lebenszyklus der Clients: Sie können Peers hinzufügen, deaktivieren und löschen, ohne eine Konfigurationsdatei per SSH zu bearbeiten. Dafür geben Sie die direkte Kontrolle über diese Konfiguration auf. Dieses Thema wird in der manuellen WireGuard-Einrichtung auf einem VPS behandelt.

Sie benötigen einen KVM-VPS mit öffentlicher IPv4-Adresse, Docker Engine mit dem Compose-Plugin und root-Zugriff. Container-Virtualisierung, die den Kernel des Hosts verwendet, kann das WireGuard-Modul in der Regel nicht laden. Der Container kann die Schnittstelle dann nicht aktivieren.

Version 15 verwendet die Einstellungen nicht mehr aus der Umgebung

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 Administratorkennworts. Beide Werte werden als Umgebungsvariablen gesetzt. Version 15 ist eine Neuentwicklung. Die offiziellen Migrationshinweise stellen klar, dass v15 nicht dieselben Umgebungsvariablen wie v14 verwendet und dass die meisten dieser 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 Administratorkonto anzulegen. Das ist kein Fehler. Dies ist der neue Einrichtungsablauf.

Stand Juli 2026 sollten Sie den Major-Tag 15 festlegen. Verwenden Sie nicht latest, da ein Major-Upgrade das Konfigurationsformat auf dem Datenträger ändert und kein sauberes Rollback ermöglicht.

Die Compose-Datei

Erstellen Sie ein Verzeichnis für den Stack und schreiben Sie die offizielle Compose-Datei dort hinein. Dies ist die unveränderte Upstream-Datei.

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, das den Serversschlüssel und alle von Ihnen erstellten Clients enthält. Sichern Sie dieses Volume, da bei einem Neuaufbau alle Peers verloren gehen. Wenn Sie diese Dateien stattdessen im Host-Dateisystem sehen möchten, ersetzen Sie es 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 erforderlich sind

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, das wg0-Interface zu erstellen, ihm eine Adresse zuzuweisen und Routen zu schreiben. Ohne diese Berechtigung startet der Container und beendet sich anschließend beim Aktivieren des Interfaces, weil ip link add wg0 type wireguard 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 und nicht im Image. Deshalb muss das Host-Verzeichnis sichtbar sein. Bei einem modernen Kernel ist das Modul normalerweise bereits integriert. Sie können dies auf dem Host mit sudo modprobe wireguard && echo ok überprüfen.

net.ipv4.ip_forward=1 weist den Kernel an, Pakete weiterzuleiten, die nicht an den Host selbst adressiert sind. Ohne diese Einstellung stellt ein Client eine Verbindung her, der Handshake ist erfolgreich, und anschließend wird jedes Paket ins Internet verworfen. Deshalb läuft ping 1.1.1.1 mit einem Timeout ab, obwohl die VPN-Verbindung als aktiv angezeigt wird.

net.ipv4.conf.all.src_valid_mark=1 ist die Einstellung, die viele überrascht. WireGuard markiert seine eigenen ausgehenden Pakete, damit sie nicht zurück in den Tunnel geroutet werden. Eine strikte Reverse-Path-Filterung 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. Upstream weist darauf hin, dass start bei einem Container, der mit anderen Einstellungen erstellt wurde, das Netzwerk in einen inkonsistenten Zustand versetzt. Wenn der Stack nach einem Reboot wieder gestartet werden soll, deckt restart: unless-stopped dies bereits ab. Das Startverhalten von Compose-Diensten erläutert, 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 die Zeile Endpoint übernommen. 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 übergeben, 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 INSECURE=true nicht 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 ist dagegen nicht möglich.

Veröffentlichen Sie den UI-Port nicht im Internet

Die Compose-Datei veröffentlicht Port 51821 auf jeder Schnittstelle. Das ist eine Anmeldeseite für ein System, das Ihren Netzwerkverkehr weiterleiten kann. Sie sollte nicht öffentlich erreichbar sein. Beim Veröffentlichen eines Ports in Docker werden Regeln in die Kette DOCKER geschrieben. Diese wird vor ufw ausgewertet. Eine deny-Regel in ufw schließt den Port daher nicht. Dieses Problem ist eigenständig wichtig. Warum veröffentlichte Docker-Ports ufw ignorieren erklärt die Hintergründe 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. Der Datenverkehr wird durch SSH verschlüsselt. Der Port antwortet keinem anderen System. INSECURE=true ist hier sicher, weil der unverschlüsselte HTTP-Hop die loopback-Schnittstelle nicht verlässt.

UDP 51820 öffnen und beide Firewalls prüfen

WireGuard muss über UDP 51820 aus dem Internet erreichbar sein. Docker veröffentlicht den Port, aber viele Provider schalten vor dem VPS eine separate Netzwerk-Firewall, von der Docker nichts weiß. Öffnen Sie den Port an beiden Stellen. Wenn Sie die Host-Firewall mit ufw verwalten, sind die grundlegenden ufw-Regeln für einen VPS der kürzere Weg, als nftables von Hand 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 das Interface nicht aktiviert. sudo docker compose logs wg-easy nennt den Grund.

Erstellen Sie einen Client und scannen Sie ihn mit einem Smartphone

Erstellen Sie in der Benutzeroberfläche einen Client und geben Sie ihm einen Namen, den Sie später wiedererkennen, beispielsweise den Namen des zugehörigen Geräts. wg-easy weist die nächste freie Tunneladresse zu und generiert das Schlüsselpaar automatisch. Jede Clientzeile enthält einen QR-Code und eine herunterladbare .conf-Datei.

Installieren Sie die offizielle WireGuard-App auf dem Smartphone. 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 unter dem eingegebenen Namen angezeigt. Aktivieren Sie ihn. In der Clientzeile der Benutzeroberfläche werden nun Übertragungszähler und ein aktueller Handshake-Zeitpunkt angezeigt. Sobald ein Smartphone den Tunnel verwendet, kann es Dienste erreichen, die Sie nie im Internet veröffentlicht haben. So kann das Smartphone von überall weiter Dateien auf einen selbst gehosteten Fotoserver hochladen, ohne dass dieser Server einen einzigen Port für die Außenwelt öffnen muss. Dasselbe gilt für Medien. Auch eine als Videothek der 90er-Jahre neu aufgebaute Jellyfin-Bibliothek 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 Smartphone sofort eine Nachricht senden, wenn ein Backup-Auftrag fehlschlägt, ohne jemals eine Anfrage aus dem öffentlichen Internet zu beantworten.

Ein Client, der nach der Aktivierung keinen Handshake anzeigt, erreicht den Server überhaupt nicht. Die Ursache liegt dann bei UDP 51820, entweder in der Provider-Firewall oder in der in der Konfiguration hinterlegten Endpoint-Adresse. Ein Client mit Handshake, aber ohne funktionierenden Internetzugang weist 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, statt sie erneut abzutippen. 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 UI nicht mehr ausreicht

wg-easy ist das passende Tool, solange Ihre Peers Personen und Telefone sind. Die UI ist schneller als das Bearbeiten von Konfigurationsdateien, und den Zugriff eines verlorenen Telefons widerrufen Sie mit einem Klick.

Die Grenzen erreichen Sie, sobald Sie etwas benötigen, das in der UI nicht abgebildet wird. Site-to-Site-Routing, bei dem der AllowedIPs eines Peers ein gesamtes entferntes Subnetz statt einer einzelnen Adresse abdeckt, ist meistens die erste Hürde. Split-Tunnel mit Peer-spezifischen Routing-Regeln oder eine von Ihrem Provisioning-Tool erzeugte Konfiguration folgen als Nächstes. Ab diesem Punkt ist die manuelle Einrichtung nicht schwieriger, sondern lediglich anders. Der einfache WireGuard-Leitfaden zeigt denselben Tunnel mit wg0.conf. Wenn Sie die Control Plane überhaupt nicht mehr betreiben möchten, beschreibt WireGuard im Vergleich zu Tailscale die verwaltete Option. Ob dieser Tausch sinnvoll ist, hängt davon ab, welche Systeme der Koordinationsserver tatsächlich erreichen kann. Lesen Sie das Vertrauensmodell von Tailscale, bevor Sie ihm Zugriff auf Ihr Netzwerk geben. Die Kosten sind meist die nächste Frage. Was der kostenlose Tailscale-Tarif tatsächlich abdeckt zeigt, dass ein Haushalt oder ein kleines Team dafür nichts bezahlen muss. Danach werden Benutzer statt Geräte abgerechnet. Das ist eine andere Kostenstruktur als bei einem VPS, den Sie bereits bezahlen. Prüfen Sie daher was Tailscale kostet, sobald der kostenlose Tarif nicht mehr ausreicht, bevor Sie ein Team migrieren. Der gerade eingerichtete vollständige Tunnel hat dort ein direktes Gegenstück. Wenn Sie den VPS als Tailscale-Exit-Node bekanntgeben, erhalten Sie dieselbe Route über den Server. Sie wird jedoch in der Admin-Konsole freigegeben, statt in jeder Client-Konfiguration eingetragen zu werden. Auch für die Subnetz-Grenze gibt es ein Gegenstück. Wenn Sie ein gesamtes privates Netzwerk vom VPS bekanntgeben, wird dieses Netzwerk an jedes Gerät im Tailnet verteilt. Die peerbezogene AllowedIPs-Bearbeitung, die Sie von der UI weggeführt hat, entfällt. Wenn Sie dieses Dashboard und das automatische Mesh-Routing möchten, aber keinen fremden Koordinationsserver verwenden wollen, hält der Betrieb eines eigenen NetBird-Servers auf einem VPS die Control Plane auf Hardware, die Sie selbst besitzen. Dafür müssen Sie DNS und TLS einrichten, was wg-easy nicht von Ihnen verlangt hat.

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 vollständig neu geschrieben. Upstream hat fast die gesamte Konfiguration in das Admin-Panel der Weboberfläche verschoben. Der Container liest keine der beiden Variablen. Er startet daher normal und fordert Sie beim ersten Aufruf auf, ein Admin-Konto anzulegen. Legen Sie die für die Clients erreichbare Hostadresse stattdessen auf dieser Einrichtungsseite fest.

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

Nein. 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 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 noch in der Compose-Datei enthalten sind. Eine manuell bearbeitete Kopie verliert diese Einträge 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 wie eine vollständig ausgefallene Verbindung.

Wie sichere ich meine Clients?

Alle Daten befinden sich im benannten Volume etc_wireguard, in einer Datei wg0.json. Die Weboberfläche enthält außerdem eine Schaltfläche für Backups, die dieselben Daten exportiert. Kopieren Sie diese Datei vor jedem Upgrade an einen Ort außerhalb des Servers. Bei einem neuen Container erfolgt die Wiederherstellung während der Einrichtung durch Hochladen der Datei.

Kann ich wg-easy hinter einem Reverse Proxy betreiben?

Ja. Schalten Sie den Proxy vor TCP 51821, terminieren Sie TLS dort und setzen Sie INSECURE=true am Container, damit er die unverschlüsselte HTTP-Verbindung vom Proxy akzeptiert. Veröffentlichen Sie UDP 51820 weiterhin direkt, da der VPN-Datenverkehr UDP verwendet und nicht über einen HTTP-Proxy läuft.