WireGuard „Handshake did not complete“: Ursachen und Fix
Kein WireGuard-Handshake? Sechs Ursachen in der Reihenfolge, in der sie wirklich vorkommen: UDP-Port, Schlüssel, Endpoint, AllowedIPs, Keepalive, Uhrzeit. Mit Prüfbefehl und Fix.
Die kurze Antwort
Die Meldung Handshake for peer 1 (203.0.113.10:51820) did not complete after 5 seconds, retrying (try 2) bedeutet: Dein WireGuard-Client hat ein Handshake-Paket an den Server geschickt und innerhalb von fünf Sekunden keine Antwort bekommen. Dafür gibt es genau drei Möglichkeiten. Das Paket kommt nicht beim Server an. Der Server bekommt es, verwirft es aber stillschweigend. Oder der Server antwortet, und die Antwort findet den Weg zurück nicht. WireGuard antwortet auf ein ungültiges Paket grundsätzlich nicht, auch nicht mit einer Fehlermeldung. Deshalb sehen alle Ursachen vom Client aus gleich aus, und deshalb hilft nur, sie der Reihe nach auszuschließen.
In der Praxis kommen die Ursachen in dieser Reihenfolge vor:
- Der UDP-Port ist nicht erreichbar: Firewall im Kundenpanel des Hosters,
ufwauf dem Server, oder einListenPort, der nicht zumEndpointpasst. - Ein öffentlicher Schlüssel ist falsch, oder die beiden Schlüssel sind vertauscht.
- Der
Endpointfehlt, zeigt auf die falsche Adresse oder ist veraltet. AllowedIPslässt den Peer oder sein Ziel nicht durch.PersistentKeepalivefehlt, und der Client sitzt hinter NAT.- Die Uhr des Clients ist seit dem letzten Handshake zurückgesprungen.
Zu jeder Ursache steht unten der Befehl, der sie nachweist, und der Fix. Vorausgesetzt ist eine fertige wg0.conf auf beiden Seiten. Wer noch beim Aufsetzen ist, findet Schlüssel, Interface-Datei, NAT und Firewall in der Anleitung zum eigenen WireGuard-VPN auf dem VPS.
Woher die Meldung kommt und warum Linux sie versteckt
Die fünf Sekunden sind der Wert REKEY_TIMEOUT aus dem WireGuard-Protokoll. Nach jedem Ablauf schickt der Client ein neues Handshake-Paket, insgesamt 90 Sekunden lang. Dann gibt er auf und schreibt did not complete after 20 attempts, giving up. Einen neuen Versuch startet er erst, wenn wieder ein Paket durch den Tunnel will. Ein ping auf die Tunnel-Adresse des Servers reicht dafür.
In den WireGuard-Apps für Windows, macOS, iOS und Android steht die Meldung im Protokoll der App, mit einer anderen Kennung des Peers davor, sonst gleichlautend. Unter Linux schreibt das Kernel-Modul sie nur, wenn du das Debug-Logging für das Modul einschaltest. Ohne diesen Schritt bleibt dmesg leer, egal wie oft der Handshake scheitert.
echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control
sudo journalctl -k -fMit -p statt +p schaltest du es wieder ab. Für diese Fehlersuche ist das Debug-Log das wichtigste Werkzeug, denn nur dort sagt der Server, warum er ein Paket verworfen hat. Die Meldungen sind unten bei der jeweiligen Ursache zitiert. Schalte es auf beiden Seiten ein, wenn beide Linux sind.
Ein Hinweis zu den Ausgaben in diesem Beitrag
Alle Prüfbefehle hier lesen den Netzwerkzustand eines echten Servers mit einem echten Gegenüber aus, mit einer Firewall beim Hoster oder einem NAT-Router zu Hause dazwischen. So etwas lässt sich in einer isolierten Testumgebung nicht nachstellen. Deshalb stehen hier die Felder der Ausgaben statt abgedruckter Beispielausgaben, und dieser Beitrag trägt kein Prüfsiegel. Die Befehle laufen bei dir, und die Ausgaben stammen von deinem Server.
Die drei Werkzeuge: wg show, ss und journalctl
wg und wg-quick kommen aus dem Paket wireguard-tools. Auf Ubuntu 24.04 und Ubuntu 26.04 heißt es so, auf Debian 12 und Debian 13 ebenfalls. Das Paket wireguard ist nur ein Metapaket, das wireguard-tools nachzieht. Das Kernel-Modul ist seit Linux 5.6 Teil des Kernels, es gibt also nichts weiter zu installieren. Ubuntu 26.04 liefert mit Stand September 2026 ein neueres wireguard-tools als 24.04; die Befehle in diesem Beitrag sind auf beiden gleich.
sudo apt update && sudo apt install -y wireguard-tools tcpdumpsudo wg show zeigt pro Interface den öffentlichen Schlüssel und das Feld listening port, und pro Peer die Felder endpoint, allowed ips, latest handshake, transfer und persistent keepalive. Zwei Dinge daran entscheiden die Fehlersuche. Erstens: Die Zeile latest handshake fehlt ganz, solange seit dem Start des Interfaces kein Handshake gelungen ist. Zweitens: Auf dem Server bekommt ein Peer das Feld endpoint erst, wenn mindestens ein gültiges Paket von ihm angenommen wurde. Fehlt es, hat der Server diesen Client noch nie akzeptiert. Steht es da, sind Port und Schlüssel in Ordnung, und der Fehler liegt weiter hinten. Das Feld transfer zählt nur Bytes, die der Server einem Peer zuordnen konnte. Verworfene Pakete tauchen dort nie auf.
Für Skripte gibt es die Felder einzeln. sudo wg show wg0 latest-handshakes gibt pro Peer den Zeitpunkt als Unix-Zeit aus, 0 heißt nie. sudo wg show wg0 endpoints listet die Endpoints, sudo wg show wg0 public-key den eigenen öffentlichen Schlüssel.
sudo ss -ulnp listet alle UDP-Sockets, auf denen der Kernel lauscht. Der WireGuard-Port steht darin ohne Prozessnamen, weil der Socket dem Kernel-Modul gehört und keinem Programm. Das ist normal und kein Fehler.
sudo ss -ulnp | grep 51820journalctl -u wg-quick@wg0 zeigt, was wg-quick beim Start ausgeführt hat, und jeden Fehler, an dem das Interface gescheitert ist. Ein Interface, das gar nicht hochkommt, kann keinen Handshake machen, also gehört dieser Blick an den Anfang.
sudo systemctl status wg-quick@wg0
sudo journalctl -u wg-quick@wg0 -n 50Die häufigsten Startfehler: Key is not the correct length or format heißt, ein Schlüssel in der Datei hat ein Leerzeichen, einen Zeilenumbruch oder ein fehlendes = am Ende. Line unrecognized heißt Tippfehler in einem Schlüsselwort, etwa AllowedIPS. Address already in use heißt, ein anderer Prozess oder ein zweites WireGuard-Interface hält den Port schon.
Ursache 1: Das UDP-Paket erreicht den Server nicht
Das ist die häufigste Ursache, und sie hat drei Varianten. Alle drei prüfst du mit einem einzigen Befehl auf dem Server, während der Client seinen Handshake versucht:
sudo tcpdump -ni any udp port 51820Ein Handshake-Paket ist 148 Bytes lang, tcpdump zeigt es als UDP, length 148. Die Antwort des Servers hat 92 Bytes. Siehst du ankommende Pakete mit 148 Bytes von der Adresse deines Clients, ist Ursache 1 erledigt, weiter zu Ursache 2. Siehst du nichts, kommt das Paket nicht an, und eine der drei Varianten ist schuld.
Die Firewall beim Hoster. Viele Anbieter haben eine zweite Firewall im Kundenpanel, die vor dem Server sitzt. Auf dem Server selbst ist sie unsichtbar: ufw kann offen sein, ss kann den Port zeigen, und das Paket kommt trotzdem nie an, weil es schon vor dem Server verworfen wird. Prüfe im Panel, ob UDP 51820 eingehend erlaubt ist. Eine Vorlage, die nur TCP 22, 80 und 443 öffnet, blockiert jeden UDP-Port.
ufw auf dem Server. sudo ufw status verbose zeigt die Regeln und die Standardrichtlinie. Eingehend steht sie auf deny, und ohne eigene Regel für den WireGuard-Port gilt sie auch für ihn. Ist das Logging von ufw an, was die Standardeinstellung ist, steht jedes verworfene Paket in journalctl -k als Zeile mit [UFW BLOCK], PROTO=UDP und DPT=51820. Der Fix ist eine Zeile:
sudo ufw allow 51820/udp
sudo ufw status verbose51820/tcp hilft nicht. WireGuard spricht ausschließlich UDP. Wer ufw zum ersten Mal auf einem Server anfasst, findet Richtlinie, Regelreihenfolge und das Rate-Limiting für SSH in den ufw-Grundlagen für einen VPS. Debian installiert ufw nicht vor; wer dort direkt mit nftables arbeitet, sucht mit sudo nft list ruleset nach einer Regel mit udp dport 51820 accept in der Input-Chain. Läuft WireGuard in einem Container, etwa mit wg-easy unter Docker, muss das Port-Mapping 51820:51820/udp heißen. Ohne das /udp mappt Docker nur TCP, und der Container bekommt kein einziges Handshake-Paket.
Falscher ListenPort. Der ListenPort in der [Interface]-Sektion des Servers muss der Port hinter dem Doppelpunkt im Endpoint des Clients sein. Fehlt ListenPort ganz, wählt der Kernel bei jedem Start einen zufälligen Port, und der Client trifft ihn nur mit Glück. sudo ss -ulnp | grep 51820 zeigt, ob der Port offen ist, und sudo wg show wg0 listen-port sagt, welchen Port WireGuard gerade wirklich nutzt.
Ein Portscanner hilft bei keiner der drei Varianten. WireGuard antwortet auf ein Paket ohne gültige Signatur nicht, also meldet nmap -sU den Port immer als open|filtered, egal ob die Firewall offen ist. Das gilt für jeden UDP-Dienst, der auf Unbekanntes schweigt; die Prüfung, ob ein Port unter Linux wirklich offen ist, läuft bei UDP deshalb über tcpdump auf der Zielseite und nicht über den Scanner.
Ursache 2: Ein Schlüssel ist falsch oder vertauscht
Jede Seite braucht den öffentlichen Schlüssel der anderen Seite. In der [Peer]-Sektion des Clients steht der öffentliche Schlüssel des Servers. In der [Peer]-Sektion des Servers steht der öffentliche Schlüssel des Clients. Vier Fehler sehen wir immer wieder: der private statt des öffentlichen Schlüssels, der eigene öffentliche Schlüssel statt dem des Gegenübers, ein Schlüssel, der beim Kopieren ein Zeichen verloren hat, und ein PresharedKey, der nur auf einer Seite steht.
Der Mechanismus dahinter: Das erste Handshake-Paket trägt eine MAC (Message Authentication Code), die mit dem öffentlichen Schlüssel des Servers berechnet wird. Hat der Client den falschen Server-Schlüssel, stimmt die MAC nicht, und der Server verwirft das Paket mit Invalid MAC of handshake, dropping packet from. Stimmt der Server-Schlüssel, aber der Server kennt den Schlüssel des Clients nicht, entschlüsselt er das Paket, findet keinen passenden Peer und verwirft es mit Invalid handshake initiation from. Beide Meldungen siehst du nur mit eingeschaltetem Debug-Log. Ohne Debug-Log ist der Server in beiden Fällen stumm, und tcpdump zeigt ankommende Pakete ohne Antwort.
# auf dem Server
sudo wg show wg0 public-key
sudo wg show wg0 peers
# auf dem Client
sudo wg show wg0 public-keyDer public-key des Servers muss genau der PublicKey in der [Peer]-Sektion des Clients sein. Der public-key des Clients muss in der peers-Liste des Servers stehen. Ein Schlüssel hat 44 Zeichen und endet mit =. Fehlt dir ein öffentlicher Schlüssel, leite ihn aus dem privaten ab. Das Ergebnis ist immer dasselbe, du kannst es also jederzeit gegenprüfen:
sudo sh -c 'wg pubkey < /etc/wireguard/server.key'Der PresharedKey fließt erst in die Antwort des Servers ein. Steht er nur auf einer Seite oder unterscheidet er sich, nimmt der Server das erste Paket an und antwortet, aber der Client kann die Antwort nicht entschlüsseln und verwirft sie mit Invalid handshake response from. Auf dem Server zeigt der Peer dann ein endpoint, aber keinen latest handshake, denn der Server setzt dieses Feld erst, wenn das erste Datenpaket des Clients mit den neuen Schlüsseln ankommt. Der Fix: derselbe PresharedKey in beiden [Peer]-Sektionen, oder in keiner.
Ursache 3: Endpoint fehlt, zeigt auf die falsche Adresse oder ist veraltet
Nur die Seite, die den Handshake beginnt, braucht einen Endpoint. Der Server hat normalerweise keinen. Er lernt Adresse und Port des Clients aus dem ersten gültigen Paket und aktualisiert beides mit jedem weiteren. Hat keine Seite einen Endpoint, beginnt niemand, und im Debug-Log steht bei jedem Sendeversuch No valid endpoint has been configured or discovered for peer.
Falsche Adresse. Der Endpoint ist die öffentliche Adresse des VPS plus der ListenPort, also 203.0.113.10:51820. Der häufigste Fehler ist die Tunnel-Adresse an dieser Stelle, 10.8.0.1:51820. Sie ist erst erreichbar, wenn der Tunnel steht, und der Tunnel steht erst nach dem Handshake. Eine IPv6-Adresse braucht eckige Klammern: [2001:db8::1]:51820. Prüfe auf dem Client mit sudo wg show, das Feld endpoint des Peers muss die öffentliche Adresse des Servers mit dem richtigen Port zeigen.
Hostname statt Adresse. wg-quick löst einen Hostnamen genau einmal auf, beim Start des Interfaces. Lässt sich der Name nicht auflösen, kommt das Interface nicht hoch, und journalctl -u wg-quick@wg0 zeigt den Fehler des Resolvers zusammen mit dem Hostnamen. Löst er sich auf, merkt sich WireGuard nur die Adresse. Ändert sich die Adresse später, etwa bei DynDNS oder nach einem Umzug des Servers, sendet der Client weiter an die alte, bis du das Interface neu startest. Für den Sofort-Fix ohne Neustart:
sudo wg set wg0 peer <öffentlicher Schlüssel des Servers> endpoint vpn.example.com:51820Für DynDNS-Setups gibt es im Quell-Repository von wireguard-tools unter contrib das Skript reresolve-dns.sh. Es läuft aus einem systemd-Timer und setzt den Endpoint neu, sobald sich die Adresse hinter dem Namen ändert.
Ursache 4: AllowedIPs lässt den Peer nicht durch
AllowedIPs macht auf beiden Seiten etwas anderes, und nur einer der beiden Fehler verhindert den Handshake wirklich. Wie WireGuard Schlüssel und Adressbereiche verknüpft, erklärt die Erklärung des Cryptokey Routing in WireGuard im Detail; hier geht es nur um die beiden Fehlerbilder.
Auf dem Client ist AllowedIPs die Routing-Tabelle. Nur Pakete an ein Ziel innerhalb von AllowedIPs gehen in den Tunnel. Pingst du 10.8.0.1, aber AllowedIPs enthält nur 10.20.0.0/16, geht das Paket über die normale Standardroute ins Internet, und WireGuard sieht es nie. Es gibt keinen Handshake, weil nichts einen ausgelöst hat. Im Debug-Log steht in dem Fall No peer has allowed IPs matching 10.8.0.1. Bei wg show fehlt latest handshake, obwohl kein einziger Versuch gescheitert ist. Der Fix: das Tunnel-Netz mit in die Liste, etwa AllowedIPs = 10.8.0.0/24, 10.20.0.0/16, oder 0.0.0.0/0 für einen Full-Tunnel.
Auf dem Server ist AllowedIPs die Zugriffsliste. Trägt die [Peer]-Sektion des Servers 10.8.0.3/32 ein, der Client hat aber Address = 10.8.0.2/32, dann gelingt der Handshake trotzdem. Beide Seiten zeigen latest handshake. Aber jedes Datenpaket des Clients hat die Quelladresse 10.8.0.2, die nicht zu diesem Peer gehört, und der Server verwirft es mit Packet has unallowed src IP (10.8.0.2) from peer 1. Der ping läuft ins Leere, und wer nur den Ping ansieht, hält es für ein Handshake-Problem. Es ist keines. Prüfe mit sudo wg show wg0 allowed-ips auf dem Server und vergleiche mit der Address des Clients. Dasselbe Bild entsteht, wenn zwei Peers dieselbe AllowedIPs haben: Die Adresse wandert zum zuletzt konfigurierten Peer, und der erste bekommt ohne Fehlermeldung keine Pakete mehr.
Ursache 5: PersistentKeepalive fehlt hinter NAT
Das Bild hier ist anders: Der Handshake gelingt, der Tunnel läuft, und nach einigen Minuten ohne Verkehr ist der Client vom Server aus nicht mehr erreichbar. Ist das Debug-Log auf dem Server an, steht dort jetzt die Meldung Handshake for peer ... did not complete, diesmal also auf dem Server, nicht auf dem Client. Sobald der Client selbst etwas sendet, geht wieder alles.
Die Ursache ist der NAT-Router (Network Address Translation) vor dem Client, also der Heimrouter oder das Mobilfunknetz. Er hält die Zuordnung zwischen dem internen Port des Clients und dem externen Port nur so lange, wie Pakete fließen. Der Linux-Standardwert dafür ist 30 Sekunden ohne Antwortverkehr, nachzulesen in /proc/sys/net/netfilter/nf_conntrack_udp_timeout, und viele Heimrouter laufen mit einem Linux-Kernel. Läuft die Zuordnung ab, sendet der Server an einen externen Port, den der Router niemandem mehr zuordnet, und der Router verwirft das Paket. Der Client merkt davon nichts, bis er selbst sendet. Dann legt der Router eine neue Zuordnung an, der Server lernt den neuen Quellport aus dem ersten gültigen Paket, und der Tunnel funktioniert wieder, ohne dass jemand etwas geändert hat. Genau dieses Verhalten ist das Kennzeichen von Ursache 5.
Der Fix steht in der [Peer]-Sektion der Seite hinter dem NAT, also normalerweise im Client:
PersistentKeepalive = 2525 Sekunden liegen unter den 30 Sekunden des Standard-Timeouts. Auf dem Server mit öffentlicher Adresse bringt die Option nichts, er hat keinen Router vor sich. Sitzt dagegen der Server selbst hinter einem NAT, etwa ein Rechner im Heimnetz, der den VPS als Gegenstelle nutzt, gilt dasselbe für ihn; das Routing per WireGuard ins eigene Heimnetz baut genau auf dieser Konstellation auf. Zur Kontrolle zeigt sudo wg show das Feld persistent keepalive mit dem Intervall, und tcpdump zeigt alle 25 Sekunden ein Paket mit 32 Bytes, das leere Datenpaket, das die Zuordnung offen hält.
Ursache 6: Die Uhr des Clients ist zurückgesprungen
Das ist die seltenste Ursache, und die, die am meisten Zeit kostet, weil kein Konfigurationswert dazu passt. Das erste Handshake-Paket enthält einen verschlüsselten Zeitstempel im Format TAI64N. Laut Protokollspezifikation merkt sich der Server pro Peer den größten je empfangenen Zeitstempel und verwirft jedes Paket mit einem Zeitstempel, der kleiner oder gleich ist. Das ist der Schutz gegen das Wiedereinspielen alter Handshake-Pakete. Unter Linux stammt der Zeitstempel aus der Systemuhr des Clients.
Die Folge: Steht die Uhr des Clients jetzt früher als beim letzten erfolgreichen Handshake, ist jeder neue Handshake aus Sicht des Servers ein Replay. Der Server verwirft ihn mit Invalid handshake initiation from, derselben Meldung wie bei einem unbekannten Schlüssel. Nur die Uhr des Clients zählt, verglichen mit seinem eigenen früheren Zeitstempel. Die Uhr des Servers wird nicht verglichen und darf beliebig falsch gehen.
So sieht das in der Praxis aus: Ein Raspberry Pi oder ein anderer Rechner ohne Hardware-Uhr startet mit einem alten Datum und bekommt die richtige Zeit erst nach dem NTP-Abgleich, der aber den Tunnel braucht. Eine virtuelle Maschine wird aus einem Snapshot zurückgesetzt. Ein Laptop hat eine leere CMOS-Batterie. Jemand hat die Uhr von Hand gestellt. Prüfe auf dem Client mit timedatectl, ob die Systemuhr synchronisiert ist und ob der NTP-Dienst aktiv ist, und vergleiche date -u auf beiden Seiten.
Zwei Fixes, je nachdem, welche Seite du erreichst. Auf dem Client die Uhr synchronisieren, mit sudo timedatectl set-ntp true; sobald sie hinter dem gespeicherten Zeitstempel liegt, gelingt der nächste Handshake. Oder auf dem Server das Gedächtnis löschen: Der gespeicherte Zeitstempel lebt nur im Arbeitsspeicher des Kernels. Entfernst du den Peer und legst ihn neu an, ist er weg.
sudo wg set wg0 peer <öffentlicher Schlüssel des Clients> remove
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg syncconf liest den Peer aus der Datei zurück, ohne die anderen Peers zu unterbrechen. Ein wg-quick down wg0 && wg-quick up wg0 löscht den Zeitstempel ebenfalls, wirft aber alle Peers raus. Damit ist auch erklärt, warum „ein Neustart des Servers hat geholfen“ so oft berichtet und so selten verstanden wird: Der Neustart hat den Zeitstempel vergessen, nicht die Uhr repariert. Beim nächsten Zurückspringen ist der Fehler wieder da.
Der Handshake steht. Was als Nächstes bricht
Zeigt wg show auf beiden Seiten einen latest handshake und ping 10.8.0.1 antwortet, bist du mit dem Handshake fertig. Die nächsten zwei Fehler kommen fast immer in dieser Reihenfolge. ping 1.1.1.1 geht, aber curl https://example.com endet mit Could not resolve host: Dann fehlt die DNS-Zeile im Client, oder der Resolver ist aus dem Tunnel nicht erreichbar, und die Fehlersuche bei DNS über WireGuard geht das der Reihe nach durch. SSH und Ping funktionieren, aber große Webseiten bleiben hängen: Das ist die MTU (Maximum Transmission Unit), und die Anleitung zu langsamen WireGuard-Verbindungen und der MTU zeigt, wie du den echten Wert per Bisektion findest, statt Zahlen zu raten.
FAQ
Warum zeigt wg show auf dem Server einen endpoint, aber keinen latest handshake?
Ein endpoint erscheint, sobald der Server ein gültiges erstes Handshake-Paket angenommen hat. Port und Schlüssel des Clients stimmen also. latest handshake setzt der Server erst, wenn das erste Datenpaket des Clients mit den neuen Sitzungsschlüsseln ankommt. Fehlt es, hat der Client die Antwort des Servers nicht akzeptiert oder nie bekommen. Die zwei Ursachen dafür: ein PresharedKey, der nur auf einer Seite steht oder sich unterscheidet, oder eine Antwort, die auf dem Rückweg verworfen wird, etwa von einer Firewall auf dem Client oder von einem NAT-Router, dessen Zuordnung schon abgelaufen ist.
Warum sehe ich „Handshake did not complete“ unter Linux nicht im Log?
Das Kernel-Modul schreibt diese Zeile nur, wenn das Debug-Logging für das Modul aktiv ist. Schalte es mit echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control ein und lies mit sudo journalctl -k -f mit. Danach sagt der Server auch, warum er ein Paket verworfen hat: Invalid MAC of handshake bei falschem Server-Schlüssel, Invalid handshake initiation bei unbekanntem Client-Schlüssel oder zurückgesprungener Uhr, Packet has unallowed src IP bei falschen AllowedIPs. Mit -p statt +p schaltest du es wieder ab.
Kann ich mit nmap oder nc prüfen, ob der WireGuard-Port offen ist?
Nein. WireGuard antwortet auf kein Paket, das keine gültige Signatur trägt, also sieht ein Scanner bei offenem und bei geschlossenem Port dasselbe: keine Antwort. nmap -sU meldet in beiden Fällen open|filtered. Der einzige belastbare Test ist sudo tcpdump -ni any udp port 51820 auf dem Server, während der Client den Handshake versucht. Kommen Pakete mit 148 Bytes an, ist der Weg frei. Kommt nichts, blockiert die Firewall beim Hoster, ufw auf dem Server oder ein falscher Port.
Gestern ging der Handshake, heute nicht mehr. Was hat sich geändert?
Bei einer Konfiguration, die schon einmal funktioniert hat, bleiben wenige Kandidaten übrig. Die öffentliche Adresse des Servers hat sich geändert, und der Client sendet noch an die alte, weil wg-quick den Hostnamen nur beim Start auflöst. Der Client sitzt hinter NAT ohne PersistentKeepalive, und die Zuordnung im Router ist abgelaufen; dann hilft es sofort, wenn der Client selbst etwas sendet. Oder die Uhr des Clients ist zurückgesprungen, etwa nach einem Neustart ohne Hardware-Uhr oder nach dem Zurücksetzen einer virtuellen Maschine; dann verwirft der Server jeden Handshake als Replay, bis die Uhr wieder stimmt oder du den Peer auf dem Server neu anlegst.
Hilft ein Neustart des Servers?
Nur in zwei Fällen, und beide sind oben erklärt. Er löscht den pro Peer gespeicherten Zeitstempel, also verschwindet Ursache 6 vorübergehend. Und er löst einen Hostnamen im Endpoint neu auf, falls der Server selbst einen konfiguriert hat. Bei einer gesperrten Firewall, einem falschen Schlüssel, einem falschen Endpoint oder falschen AllowedIPs ändert ein Neustart nichts, denn die Ursache steht in der Datei und überlebt ihn.