WireGuard-VPN auf dem eigenen Server einrichten
Vom leeren Server zum funktionierenden WireGuard-Tunnel: Schlüsselpaare, NAT mit nftables, wg-quick als Dienst, QR-Code fürs Telefon und die drei Fehlerbilder, an denen es wirklich hängt.
Was auf allen vier Distributionen gleich ist, und was nicht
WireGuard steckt seit Kernel 5.6 fest im Linux-Kernel. Auf Debian 13, Debian 12, Ubuntu 24.04 und Ubuntu 22.04 müssen Sie deshalb kein DKMS-Modul mehr bauen, kein Backport-Repository einbinden und keinen Fremdschlüssel importieren. Alle vier liefern zudem denselben Stand der Userland-Werkzeuge (Upstream-Version 1.0.20210914), das heißt: Die Befehle wg und wg-quick verhalten sich überall identisch.
Die Unterschiede liegen ausschließlich drumherum, und genau dort scheitern die meisten Anleitungen:
- Paketfilter:
wireguard-toolsempfiehlt nftables oder iptables. Da apt die erste verfügbare Alternative zieht, landet auf einer schlanken Debian-Installation nftables, nicht iptables. Die überall abgeschriebeneniptables -t nat -A POSTROUTING-Zeilen laufen dort ins Leere, solange Sie das Paket nicht nachinstallieren. - DNS-Übergabe:
wg-quickruft für die ZeileDNS =stur das Programmresolvconfauf. Auf Ubuntu liefert das Paketsystemd-resolvedeinen Kompatibilitäts-Aufsatz mit, auf einer minimalen Debian-Installation existiert oft gar keinresolvconf. Zum Nachinstallieren heißt das passende Paket auf Debian und auf Ubuntu 22.04openresolv, auf Ubuntu 24.04 gibt es dieses Paket nicht mehr. Das betrifft nur Linux-Clients, nicht Telefone. - Firewall-Frontend: Ubuntu-Images bringen häufig ein aktives ufw mit, Debian in der Regel nicht. ufw blockiert Weiterleitung standardmäßig, auch wenn
net.ipv4.ip_forwardauf 1 steht.
Prüfen Sie zuerst, was Sie vor sich haben:
apt-get update
apt-get install -y wireguard wireguard-tools nftables qrencode
wg --version
apt-cache policy wireguard-tools
Meldet wg --version eine Versionsnummer, ist das Userland da. Ob der Kernel mitspielt, sehen Sie erst beim ersten wg-quick up. Bleibt dabei die Meldung RTNETLINK answers: Operation not supported oder Unable to access interface: Protocol not supported stehen, läuft ein Kernel ohne WireGuard-Unterstützung, typischerweise ein sehr alter oder ein stark abgespeckter Container-Kernel.
Schlüsselpaare erzeugen, ohne sich Ärger einzubauen
WireGuard kennt keine Benutzernamen und keine Zertifikate. Es gibt pro Teilnehmer genau ein Schlüsselpaar, dazu optional einen gemeinsamen Preshared Key als zusätzliche symmetrische Schicht. Erzeugen Sie beides mit gesetzter umask, sonst liegen die privaten Schlüssel für alle lesbar herum:
umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
wg genkey | tee /etc/wireguard/telefon.key | wg pubkey > /etc/wireguard/telefon.pub
wg genpsk > /etc/wireguard/telefon.psk
ls -l /etc/wireguard
Das Verzeichnis /etc/wireguard müssen Sie dafür nicht selbst anlegen, das Paket wireguard-tools bringt es bereits mit Modus 0700 mit. Wichtiger ist eine Eigenheit von umask: Der Wert gilt nur in der Shell-Sitzung, in der Sie ihn setzen. Wer die Schlüssel in zwei Etappen erzeugt, etwa weil die Verbindung zwischendurch abgerissen ist und Sie sich neu angemeldet haben, arbeitet danach wieder mit der Standardmaske, und server.key sowie telefon.psk landen mit 0644 auf der Platte. Setzen Sie die Rechte deshalb zum Abschluss noch einmal ausdrücklich:
chmod 600 /etc/wireguard/*.key /etc/wireguard/*.psk
Drei Fehler passieren hier immer wieder:
- Öffentlichen und privaten Schlüssel vertauscht. Beide sind 44 Zeichen lange Base64-Strings und sehen identisch aus. Steht in
[Interface] PrivateKeyversehentlich ein öffentlicher Schlüssel, startet der Tunnel trotzdem, es kommt aber nie ein Handshake zustande. Ableiten lässt sich das jederzeit:wg pubkey < /etc/wireguard/server.keymuss exakt den Inhalt vonserver.pubergeben. - Zeilenumbruch mitkopiert. Aus einem Terminal per Maus kopierte Schlüssel schleppen gern Leerzeichen mit.
wgquittiert das mitKey is not the correct length or format. - Dateirechte. Vergessen Sie
umask 077, warntwg-quickbeim Start mitWarning: `/etc/wireguard/wg0.conf' is world accessible. Das ist keine Kosmetik, in dieser Datei steht der private Schlüssel im Klartext.
Die Serverkonfiguration
Ermitteln Sie zuerst den Namen Ihrer Internet-Schnittstelle. Auf virtuellen Maschinen heißt sie je nach Image eth0, ens3 oder enp1s0:
ip route show default
Legen Sie dann /etc/wireguard/wg0.conf an. Das Tunnelnetz sollte eines sein, das Ihnen unterwegs nicht in einem Hotel-WLAN begegnet, also lieber nicht 192.168.0.0/24 oder 192.168.1.0/24:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = INHALT_VON_SERVER_KEY
PostUp = nft add table ip wgnat
PostUp = nft add chain ip wgnat postrouting '{ type nat hook postrouting priority srcnat; policy accept; }'
PostUp = nft add rule ip wgnat postrouting ip saddr 10.8.0.0/24 oifname "eth0" masquerade
PostDown = nft delete table ip wgnat
[Peer]
# telefon
PublicKey = INHALT_VON_TELEFON_PUB
PresharedKey = INHALT_VON_TELEFON_PSK
AllowedIPs = 10.8.0.2/32
Ersetzen Sie eth0 durch Ihre tatsächliche Schnittstelle. Wer lieber bei iptables bleibt, installiert iptables nach und nimmt stattdessen:
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
Zwei Details, die selten erwähnt werden. Erstens: Jede PostUp-Zeile wird über eine Shell ausgeführt, und bricht eine davon mit einem Fehler ab, rollt wg-quick den gesamten Start zurück. Ein Tippfehler in der nft-Regel führt also nicht zu einem halb funktionierenden Tunnel, sondern zu gar keinem. Zweitens: AllowedIPs hat auf der Serverseite eine völlig andere Bedeutung als auf der Clientseite. Hier ist es eine Zuordnungstabelle, welche Absender-Adresse zu welchem Peer gehört. Tragen Sie bei zwei Peers dieselbe Adresse ein, gewinnt der zuletzt geladene, und der andere ist stumm. Jeder Client bekommt genau eine /32.
Setzen Sie die Rechte und prüfen Sie die Syntax, bevor Sie starten:
chmod 600 /etc/wireguard/wg0.conf
wg-quick strip wg0
wg-quick strip gibt die Konfiguration ohne die wg-quick-eigenen Zeilen aus. Läuft der Befehl durch, ist die Datei formal in Ordnung. Meldet er wg-quick: Line unrecognized, haben Sie meist eine Option in den falschen Abschnitt geschrieben, etwa DNS unter [Peer].
IP-Weiterleitung dauerhaft einschalten
Ohne Weiterleitung endet jedes Paket im Server. Sie schalten sie sofort und dauerhaft ein:
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-wireguard.conf
sysctl --system
sysctl net.ipv4.ip_forward
Der letzte Befehl muss net.ipv4.ip_forward = 1 ausgeben. Wenn Sie IPv6 durch den Tunnel schicken wollen, gehört zusätzlich net.ipv6.conf.all.forwarding=1 in dieselbe Datei.
Läuft ufw, reicht das noch nicht. ufw setzt eine eigene Weiterleitungsrichtlinie, die unabhängig vom Kernel-Schalter greift. Öffnen Sie den Port und erlauben Sie den Durchgang explizit:
ufw allow 51820/udp
ufw route allow in on wg0 out on eth0
Das typische Fehlerbild bei vergessener Weiterleitung ist besonders tückisch: Der Handshake klappt, der Ping auf 10.8.0.1 klappt, aber nichts dahinter ist erreichbar. Wer nur auf den Handshake schaut, sucht dann stundenlang an der falschen Stelle.
wg-quick als Dienst einrichten
Das Paket bringt eine Template-Unit mit, der Name nach dem @ ist der Name der Konfigurationsdatei ohne Endung:
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
systemctl status wg-quick@wg0
journalctl -u wg-quick@wg0 -n 50 --no-pager
Eine Falle, die viele erst nach Wochen bemerkt: SaveConfig = true. Diese Option schreibt beim Stoppen des Dienstes den laufenden Zustand zurück in die Datei. Dabei gehen alle Kommentare, alle PostUp-Zeilen in ihrer ursprünglichen Reihenfolge und jede von Hand eingefügte Struktur verloren. Für einen Server, dessen Konfiguration Sie von Hand pflegen, lassen Sie die Option weg.
Nach dem Start prüfen Sie den Zustand nicht am Exit-Code, sondern an der Schnittstelle:
wg show
ip -brief address show wg0
ss -ulpn
ss -ulpn muss einen Lauscher auf UDP-Port 51820 zeigen. wg show listet die Peers, zu diesem Zeitpunkt noch ohne Handshake.
Client-Konfiguration und QR-Code für das Telefon
Die Client-Datei erzeugen Sie am besten auf dem Server, weil dort ohnehin alle Schlüssel liegen. Achtung: Auf der Clientseite bedeutet AllowedIPs etwas anderes, nämlich welche Ziele durch den Tunnel gehen sollen. 0.0.0.0/0, ::/0 heißt: alles.
mkdir -p /etc/wireguard/clients
Inhalt von /etc/wireguard/clients/telefon.conf:
[Interface]
PrivateKey = INHALT_VON_TELEFON_KEY
Address = 10.8.0.2/32
DNS = 9.9.9.9, 149.112.112.112
[Peer]
PublicKey = INHALT_VON_SERVER_PUB
PresharedKey = INHALT_VON_TELEFON_PSK
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = ihre.server.adresse:51820
PersistentKeepalive = 25
PersistentKeepalive = 25 ist bei Telefonen kein Luxus. Mobilfunk-NAT vergisst UDP-Zuordnungen oft nach 30 bis 60 Sekunden, ohne Keepalive kann der Server eine bestehende Verbindung nicht mehr von sich aus ansprechen.
Den QR-Code erzeugen Sie direkt im Terminal:
qrencode -t ansiutf8 < /etc/wireguard/clients/telefon.conf
In der WireGuard-App tippen Sie auf das Plus, wählen den Import per QR-Code und halten die Kamera auf das Terminal. Zwei praktische Hinweise: Verkleinern Sie die Schrift im Terminal, bevor Sie den Code erzeugen, sonst passt er nicht ins Bild. Und löschen Sie die Datei danach nicht sofort, Sie brauchen sie beim Gerätewechsel wieder. Wer sie doch löscht, muss ein neues Schlüsselpaar erzeugen, denn der private Schlüssel lässt sich aus dem öffentlichen nicht zurückrechnen.
Woran Sie erkennen, dass es wirklich läuft
Dass systemctl start ohne Ausgabe zurückkommt, heißt nur, dass die Schnittstelle existiert. Der eigentliche Test besteht aus drei Stufen, und Sie sollten sie in dieser Reihenfolge durchgehen:
wg show wg0 latest-handshakes
wg show wg0 transfer
Stufe eins, der Handshake. latest-handshakes gibt einen Unix-Zeitstempel pro Peer aus. Steht dort 0, ist noch nie eine Verbindung zustande gekommen. In der ausführlichen Ansicht von wg show lesen Sie stattdessen latest handshake: 42 seconds ago.
Stufe zwei, der Datenfluss. Bei transfer stehen empfangene und gesendete Bytes. Nur gesendete Bytes ohne empfangene bedeutet: Ihre Pakete gehen raus, es kommt nichts zurück. Das ist fast immer eine Firewall oder ein falscher Endpoint, nie ein Schlüsselproblem.
Stufe drei, der tatsächliche Weg. Auf dem Client prüfen Sie, ob der Verkehr überhaupt in den Tunnel geleitet wird:
ip route get 1.1.1.1
Steht dort dev wg0, stimmt das Routing. Erst danach ist ein Blick auf eine Seite sinnvoll, die Ihre öffentliche IP-Adresse anzeigt. Zeigt sie die Adresse Ihres Servers, sind Sie fertig.
Fehlersuche: kein Handshake
Das häufigste Fehlerbild überhaupt. Schalten Sie zuerst die Protokollierung des Kernelmoduls ein, sie liefert die Antwort meist in zehn Sekunden:
echo 'module wireguard +p' > /sys/kernel/debug/dynamic_debug/control
dmesg -w
Jetzt starten Sie am Client einen Verbindungsversuch und lesen mit. Die drei Meldungen, auf die es ankommt:
wireguard: wg0: Handshake for peer 1 (...) did not complete after 5 seconds, retrying (try 2)und sonst nichts. Beim Server kommt kein einziges Paket an. Prüfen Sie mittcpdump -n -i eth0 udp port 51820gegen. Sehen Sie dort nichts, liegt es an der Firewall vor dem Server, am Port oder daran, dass der Client in einem Netz sitzt, das ausgehendes UDP filtert. Ein Test aus dem Mobilfunknetz statt aus dem Firmen-WLAN trennt diese Fälle sauber.wireguard: wg0: Invalid handshake initiation from .... Pakete kommen an, passen aber nicht. Fast immer stimmt der öffentliche Schlüssel des Servers in der Client-Datei nicht, oder der Preshared Key ist nur auf einer Seite eingetragen. Ein Preshared Key muss auf beiden Seiten identisch sein oder auf beiden Seiten fehlen.- Gar keine Meldung, obwohl
tcpdumpPakete zeigt. Dann hört WireGuard auf einem anderen Port oder auf einer anderen Adresse. Kontrolle überss -ulpn.
Ein selten dokumentierter Sonderfall ist die Uhrzeit. WireGuard packt in die erste Handshake-Nachricht einen Zeitstempel und der Server merkt sich pro Peer den höchsten je gesehenen Wert. Ältere Zeitstempel verwirft er, das ist der Schutz gegen Wiedereinspielung. Hat ein Gerät seine Uhr weit in die Zukunft gestellt und einmal verbunden, kommt es nach dem Korrigieren der Uhr nicht mehr durch. Der Server hält den Zustand im Speicher, also hilft: Uhr richtig stellen, dann auf dem Server wg-quick down wg0 und wg-quick up wg0. Nach dem Neuaufbau ist die Sperre weg.
Schalten Sie die Protokollierung danach wieder ab, sie ist gesprächig:
echo 'module wireguard -p' > /sys/kernel/debug/dynamic_debug/control
Fehlersuche: DNS läuft nicht
Symptom: Der Tunnel steht, ping 1.1.1.1 geht, aber kein Name lässt sich auflösen. Es gibt genau drei Ursachen.
Erstens: Der Resolver ist nicht erreichbar. Wenn Sie DNS = 10.8.0.1 eintragen, muss auf dem Server tatsächlich ein Nameserver auf dieser Adresse lauschen. Ein blankes Debian oder Ubuntu hat keinen. Entweder Sie tragen einen öffentlichen Resolver ein, der über den Tunnel erreicht wird, oder Sie richten selbst einen ein. Für den zweiten Weg genügt dnsmasq mit einer kleinen Datei unter /etc/dnsmasq.d/wireguard.conf:
interface=wg0
bind-dynamic
no-resolv
server=9.9.9.9
server=1.1.1.1
cache-size=1000
bind-dynamic ist wichtig, weil wg0 beim Start von dnsmasq unter Umständen noch nicht existiert. no-resolv ist auf Ubuntu Pflicht: Ohne diese Zeile liest dnsmasq /etc/resolv.conf, findet dort die Stub-Adresse 127.0.0.53 von systemd-resolved und baut eine Schleife. Und der Punkt, der auf Ubuntu am häufigsten zubeißt: dnsmasq belegt Port 53. Auf einem System mit aktivem systemd-resolved ist dieser Port bereits vergeben, beide Dienste kollidieren, und dnsmasq startet nicht. Die Zeilen interface=wg0 und bind-dynamic sind deshalb nicht Kosmetik, sondern verhindern, dass sich dnsmasq an alle Adressen bindet. Prüfen Sie anschließend mit ss -ulpn, dass sich dnsmasq und systemd-resolved nicht um Port 53 streiten.
Zweitens: Der Resolver liegt außerhalb der AllowedIPs. Wer statt 0.0.0.0/0 nur einzelne Netze durch den Tunnel schickt und als DNS-Server eine Adresse einträgt, die nicht in dieser Liste steht, schickt die Anfragen am Tunnel vorbei ins lokale Netz.
Drittens, nur auf Linux-Clients: resolvconf fehlt. Die Zeile DNS = wird von wg-quick an ein Programm namens resolvconf weitergereicht. Fehlt es, bricht der Start ab mit /usr/bin/wg-quick: line 32: resolvconf: command not found. Prüfen Sie zuerst, ob das Programm überhaupt vorhanden ist:
command -v resolvconf
Beim Nachinstallieren gehen die Distributionen auseinander, und genau daran scheitern abgeschriebene Anleitungen auf Ubuntu 24.04. Dort existiert das Paket openresolv weder in main noch in universe, der Aufruf endet mit E: Package 'openresolv' has no installation candidate:
| System | Passendes Paket |
|---|---|
| Debian 11, Debian 12, Debian 13 | apt-get install -y openresolv |
| Ubuntu 22.04 | apt-get install -y openresolv |
| Ubuntu 24.04 | apt-get install -y resolvconf |
Auf Ubuntu 24.04 ist resolvconf ein virtuelles Paket, das eindeutig auf systemd-resolved auflöst und dabei /usr/sbin/resolvconf anlegt, also genau die Binärdatei, die wg-quick für die DNS-Zeile aufruft. Sie können dort ebenso gut direkt systemd-resolved installieren. Wer eine Zeile möchte, die auf allen genannten Systemen funktioniert, nimmt diese:
apt-get install -y openresolv || apt-get install -y resolvconf
Auf Ubuntu 24.04 gibt es eine Variante davon, die nach einer Aktualisierung von 22.04 auftaucht: Failed to resolve interface "tun.wg0": No such device. Ursache ist ein zurückgebliebenes altes resolvconf-Paket aus 22.04 samt /etc/resolvconf/interface-order, also nicht das virtuelle Paket gleichen Namens aus 24.04. Anhand dieser Datei stellt wg-quick dem Schnittstellennamen ein tun. voran, womit der Kompatibilitäts-Aufsatz von systemd-resolved nichts anfangen kann. Lösung ist das Entfernen des alten Pakets, sodass nur noch der Aufsatz von systemd-resolved übrig bleibt.
Fehlersuche: MTU-Probleme
Das unangenehmste Fehlerbild, weil alles zu funktionieren scheint. Der Handshake steht, Ping läuft, SSH läuft, aber Webseiten bauen sich halb auf und bleiben hängen, große Downloads brechen ab, und ausgerechnet HTTPS ist betroffen. Der Grund: Kleine Pakete passen, große nicht.
WireGuard packt um jedes Paket 60 Byte zusätzlich, wenn der Tunnel über IPv4 läuft (20 Byte IP, 8 Byte UDP, 32 Byte WireGuard), und 80 Byte über IPv6. wg-quick zieht deshalb pauschal 80 Byte von der ermittelten Pfad-MTU ab und landet bei einer normalen 1500er-Strecke auf 1420. Das ist bewusst konservativ und stimmt in den meisten Fällen.
Es stimmt nicht, wenn der Weg schmaler ist als 1500, etwa bei DSL mit PPPoE (1492), hinter einem weiteren Tunnel oder in manchen Mobilfunknetzen. Messen Sie die tatsächliche Pfad-MTU vom Client aus zur öffentlichen Adresse des Servers, mit gesetztem Don't-Fragment-Bit und ohne Tunnel:
ping -M do -s 1472 -c 3 ZIELADRESSE
1472 plus 28 Byte Kopfdaten ergibt 1500. Kommt ping: local error: message too long, mtu=... oder Frag needed and DF set zurück, senken Sie den Wert schrittweise, bis es durchgeht: 1464, 1444, 1414, 1372. Zum gefundenen Wert addieren Sie 28 und ziehen 80 ab. Bei 1464 wären das also 1492 Pfad-MTU und 1412 Tunnel-MTU.
Eingetragen wird das im Abschnitt [Interface], auf der Seite, die das Problem hat:
MTU = 1412
Der schnelle Gegentest, bevor Sie lange rechnen: Setzen Sie versuchsweise MTU = 1280. Das ist die kleinste MTU, die IPv6 garantiert, und funktioniert praktisch überall. Laden Ihre Seiten damit sauber, war es die MTU, und Sie können sich in Ruhe an den optimalen Wert herantasten. Bleibt das Problem, liegt es woanders.
Auf dem Server selbst ist eine falsche MTU seltener, aber möglich: Steht dort ein Wert höher als die Strecke hergibt, sehen Sie die Auswirkung nur bei bestimmten Zielen. Ein Blick auf ip -brief address show wg0 und ip link show wg0 zeigt den aktuell gesetzten Wert.
Peers im Betrieb ändern, ohne alle rauszuwerfen
Der Reflex, nach jeder Änderung systemctl restart wg-quick@wg0 zu tippen, wirft alle bestehenden Verbindungen ab und baut die NAT-Regeln neu auf. Bei einem Server mit mehreren Nutzern ist das unnötig grob. WireGuard kann die Konfiguration im laufenden Betrieb abgleichen:
wg syncconf wg0 <(wg-quick strip wg0)
Der Befehl vergleicht die Datei mit dem laufenden Zustand und ändert nur die Unterschiede. Bestehende Peers behalten ihre Sitzung. Beachten Sie, dass die Prozesssubstitution mit <(...) eine bash oder zsh voraussetzt, in einer reinen sh scheitert sie. Einen einzelnen Peer fügen Sie auch direkt hinzu:
wg set wg0 peer OEFFENTLICHER_SCHLUESSEL allowed-ips 10.8.0.3/32
Diese Änderung lebt nur im Speicher. Schreiben Sie sie zusätzlich in die Konfigurationsdatei, sonst ist der Peer nach dem nächsten Neustart verschwunden. Das ist übrigens die häufigste Ursache für den Satz "gestern ging es noch".
Wenn Sie einen WireGuard-Endpunkt dauerhaft und mit stabiler Adresse betreiben wollen, ist ein eigener Server die naheliegende Grundlage. Bei KernelHost laufen KVM-Rootserver und Dedicated Server im Rechenzentrum maincubes Frankfurt am Main (TÜV TIER3+) in unserem eigenen Netz, PrePaid und ohne Mindestlaufzeit. Weiterführend interessant sind dazu unsere Beiträge zu SSH-Absicherung und zur Einrichtung von ufw.
Häufige Fragen
Welche WireGuard-Version steckt in Debian 13, Debian 12, Ubuntu 24.04 und Ubuntu 22.04?
Warum funktionieren die iptables-Zeilen aus vielen Anleitungen auf Debian nicht?
Der Handshake klappt, aber ich komme nicht ins Internet. Woran liegt das?
Wie erkenne ich ein MTU-Problem?
Muss ich einen eigenen DNS-Server betreiben, damit DNS im Tunnel funktioniert?
Warum kommt ein Gerät nach dem Stellen der Uhr nicht mehr durch?
2026 KernelHost GmbH. Alle Rechte vorbehalten. Diese Anleitung ist urheberrechtlich geschützt. Eine Veröffentlichung auf anderen Webseiten, auch auszugsweise oder in bearbeiteter Form, ist ohne unsere schriftliche Zustimmung nicht gestattet. Zitate mit Quellenangabe und Link sind ausdrücklich willkommen.

