Problem & Aufbau
Mein Heimnetz ist über einen WireGuard-Tunnel mit einem VPS verbunden. Der VPS hat eine feste öffentliche IP und reicht Dienste aus dem Homelab (z. B. diese Website) über Nginx Proxy Manager nach außen weiter. Am heimischen Router muss dafür kein Port geöffnet werden.
Etwa einmal am Tag sind die Dienste nicht mehr erreichbar. Der Tunnel baut sich nicht von selbst wieder auf. Ein systemctl restart wg-quick@wg0 behebt es sofort, bis zum nächsten Tag.
Besonderheit: Doppel-NAT
Mein Heimnetz hängt hinter zwei Routern: Der eigene OpenWrt-Router bildet das Heimnetz, davor sitzt der Router des Internetanbieters. Beide übersetzen Adressen (NAT), jedes Paket wird also zweimal umgeschrieben.
- Warum der VPS-Umweg? Für eine Portfreigabe von außen müsste man in beiden Routern Weiterleitungen einrichten, und die öffentliche IP wechselt trotzdem. Der Tunnel geht dagegen von innen nach außen auf. Das funktioniert durch jedes NAT, ohne eine einzige Portfreigabe.
- Warum ist das für den Fehler wichtig? Jeder Router führt eine eigene Tabelle, welche UDP-Verbindung zu welchem Gerät gehört, und löscht Einträge nach eigenen Zeitlimits. Bei Doppel-NAT gibt es also zwei Stellen, an denen die Zuordnung verloren oder nach einem Reconnect veraltet sein kann.
Wie WireGuard Verbindungen hält
WireGuard läuft über UDP und kennt keine klassische „Verbindung“. Für die Fehlersuche sind vier Mechanismen wichtig:
| Mechanismus | Was er tut |
|---|---|
| Handshake | Die Peers tauschen etwa alle 2 Minuten neue Schlüssel aus. Ein alter Handshake bedeutet einen toten Tunnel. |
| Endpoint-Roaming | Der Server merkt sich, woher das letzte gültige Paket kam, und antwortet dorthin. Er folgt so einem IP-Wechsel automatisch. |
| PersistentKeepalive | Der Client hinter NAT sendet regelmäßig ein Lebenszeichen. Das hält den NAT-Eintrag offen und meldet eine neue IP sofort. Bei Doppel-NAT muss es beide Router-Tabellen offen halten. |
| MTU | Maximale Paketgröße im Tunnel. Unterschiedliche Werte führen zu „Ping geht, Webseiten hängen“. |
Diagnose
Zustand auf dem Proxmox
wg show
interface: wg0 peer: <VPS-Key> endpoint: <VPS-IP>:<Port> allowed ips: <VPS-Tunnel-IP>/32 latest handshake: 1 minute, 9 seconds ago persistent keepalive: every 25 seconds
Handshake aktuell, Keepalive bereits gesetzt. Die naheliegendste Ursache, ein fehlendes Keepalive hinter NAT, fällt damit weg.
Gegenseite auf dem VPS
Mit grep -v PrivateKey zeigt man die Config, ohne den geheimen Schlüssel preiszugeben:
cat /etc/wireguard/wg0.conf | grep -v PrivateKey
[Interface]
Address = <VPS-Tunnel-IP>/24
MTU = 1380
ListenPort = <Port>
[Peer]
# Proxmox zuhause – bewusst OHNE Endpoint
PublicKey = <Proxmox-Key>
AllowedIPs = <Proxmox-Tunnel-IP>/32, <Heimnetz>/24Für den Proxmox ist kein fester Endpoint eingetragen. Der VPS lernt die wechselnde Heim-IP also selbst. Das ist korrekt.
MTU auf beiden Seiten
ip link show wg0 | grep mtu
Beide Seiten: mtu 1380. Ein MTU-Problem ist ausgeschlossen.
Nebenbefund: Das Interface hatte auf dem Proxmox die Nummer 89. Jeder Neustart legt wg0 neu an. Die Zahl zeigt, wie oft schon von Hand neu gestartet wurde.
| Geprüft | Ergebnis |
|---|---|
| PersistentKeepalive (Proxmox) | ✓ gesetzt, 25 s |
| Kein fester Endpoint (VPS) | ✓ korrekt |
| MTU beidseitig | ✓ 1380 / 1380 |
| Ursache des Abbruchs | ? nur im Fehlerfall messbar |
Erkenntnis
Beide Konfigurationen sind sauber. Die Ursache lässt sich nur im Moment des Ausfalls sehen. Ich brauche also ein Werkzeug, das genau dann Daten sammelt, ohne dass ich daneben sitzen muss.
Lösung: Watchdog mit Diagnose-Log
Ein kleines Bash-Skript auf dem Proxmox prüft alle 2 Minuten, ob der VPS durch den Tunnel erreichbar ist. Wenn nicht, hält es zuerst den Zustand fest und startet danach WireGuard neu. Der Tunnel ist nach höchstens etwa 2 Minuten wieder da, und der Ausfall ist dokumentiert.
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LOG=/var/log/wg-watchdog.log
VPS_TUNNEL=<VPS-Tunnel-IP>
VPS_PUBLIC=<VPS-IP>
if ! ping -c 3 -W 2 "$VPS_TUNNEL" > /dev/null 2>&1; then
{
echo "===== $(date) Tunnel down ====="
echo "--- Öffentliche IP:"; curl -4 -s --max-time 5 ifconfig.me; echo
echo "--- wg show:"; wg show wg0
echo "--- Route zum VPS:"; ip route get "$VPS_PUBLIC"
echo "--- Ping VPS ohne Tunnel:"; ping -c 2 -W 2 "$VPS_PUBLIC" | tail -2
} >> "$LOG" 2>&1
systemctl restart wg-quick@wg0
sleep 5
echo "--- Nach Neustart: $(ping -c 1 -W 2 "$VPS_TUNNEL" > /dev/null \
&& echo OK || echo IMMER NOCH DOWN)" >> "$LOG"
fiDie Platzhalter in spitzen Klammern durch die eigenen Werte ersetzen.
Zeile für Zeile erklärt
| Code | Bedeutung |
|---|---|
PATH=… | Cron startet Skripte mit minimalem Suchpfad. Ohne diese Zeile werden wg oder ip evtl. nicht gefunden. |
ping -c 3 -W 2 | 3 Pings mit je 2 Sekunden Timeout durch den Tunnel. |
> /dev/null 2>&1 | Ausgabe verwerfen. Es zählt nur, ob der Ping Erfolg hatte. |
if ! … | Das ! kehrt das Ergebnis um. Der Block läuft nur bei einem Ausfall. |
{ … } >> "$LOG" 2>&1 | Alles in den Klammern wird an das Log angehängt, Fehler inklusive. |
$(date) | Zeitstempel. Daran sieht man, ob die Ausfälle immer zur gleichen Uhrzeit passieren. |
curl -4 ifconfig.me | Aktuelle öffentliche IPv4. Zeigt, ob sich die Heim-IP geändert hat. |
wg show wg0 | Endpoint und letzter Handshake im Moment des Ausfalls. |
ip route get | Welche Route der Server zum VPS nimmt. Zeigt Routing-Fehler. |
ping … | tail -2 | Ist der VPS ohne Tunnel erreichbar? Unterscheidet einen Internet-Ausfall von einem WireGuard-Problem. |
systemctl restart wg-quick@wg0 | Tunnel neu starten. |
&& … || … | Kurzform für „wenn erfolgreich, dann OK, sonst IMMER NOCH DOWN“. |
Einrichtung
Skript anlegen
Skript einfügen, speichern mit Strg+O und Enter, beenden mit Strg+X.
nano /usr/local/bin/wg-watchdog.sh
Ausführbar machen
chmod +x /usr/local/bin/wg-watchdog.sh
Cronjob anlegen
Beim ersten Aufruf fragt das System nach einem Editor, 1 = nano.
crontab -e
Ganz unten diese Zeile eintragen:
*/2 * * * * /usr/local/bin/wg-watchdog.sh
Kontrollieren
crontab -l | tail -1 head -3 /usr/local/bin/wg-watchdog.sh
cat /var/log/wg-watchdog.log meldet „No such file or directory“? Perfekt. Das Log entsteht erst beim ersten Ausfall.
Log auswerten
| Im Log | Bedeutung | Nächster Schritt |
|---|---|---|
| Neue öffentliche IP, alter Handshake | Kein neuer Handshake nach IP-Wechsel | Beide NAT-Stufen prüfen: veralteter Eintrag im OpenWrt nach Reconnect des Provider-Routers |
| VPS auch ohne Tunnel nicht erreichbar | Internetanschluss war weg | Kein WireGuard-Problem |
| Frischer Handshake, Ping scheitert | Routing oder Firewall | iptables/nftables und Routen prüfen |
| „IMMER NOCH DOWN“ | Neustart allein hilft nicht | VPS-Seite und Firewall prüfen |
| Immer gleiche Uhrzeit | Geplanter Vorgang | Mit Router-Log und Cronjobs abgleichen |
Stolperfallen
- Auf welchem Host bin ich? Der Prompt (
root@proxmoxoderroot@vps) verrät es. Ein Ping auf die eigene Tunnel-IP antwortet in 0,04 ms und sagt nichts über den Tunnel. - Nur den Befehl kopieren, nicht den Prompt und nicht die alte Ausgabe. Sonst versucht die Bash,
root@…:~#auszuführen. - Public Keys sind nicht geheim. Schützen muss man nur den Private Key, den
wg showohnehin als(hidden)ausgibt. - Diagnosetests im richtigen Moment: Läuft der Tunnel gerade, sieht jeder Test gesund aus. Deshalb sammelt der Watchdog die Daten automatisch beim Ausfall.
Fazit
Statt im Dunkeln an Einstellungen zu drehen, habe ich zuerst beide Seiten systematisch geprüft. Keepalive, Endpoint-Handling und MTU waren korrekt. Der Watchdog löst das praktische Problem sofort: Ausfälle dauern jetzt höchstens etwa 2 Minuten, und niemand muss mehr eingreifen. Gleichzeitig liefert er die Daten, um die eigentliche Ursache zu finden.
Verdacht
Am wahrscheinlichsten ist das Doppel-NAT: Wechselt der Provider-Router seine öffentliche IP, kann im OpenWrt noch ein alter UDP-Eintrag stehen, und die Pakete des Proxmox laufen ins Leere. Ein Neustart von WireGuard erzeugt einen neuen Eintrag, deshalb hilft er.
Sobald das Log den ersten Ausfall festgehalten hat, ergänze ich hier die Ursache und die endgültige Lösung.