
You ran sudo wg-quick up wg0 on your VPS and the SSH session froze. New logins time out, and if the tunnel starts at boot, a reboot brings the lockout back. What broke is the path your server uses to answer you: with AllowedIPs = 0.0.0.0/0, the VPS sends its replies to your SSH client into the tunnel instead of out through its normal network interface. Here is how to get back in, keep SSH reachable with the tunnel up and test safely, on Ubuntu 24.04 and Debian 12.
Quick answer: With AllowedIPs = 0.0.0.0/0, wg-quick makes the tunnel the default route for every packet that lacks its firewall mark, including the VPS's replies to your SSH client. Log in through your provider's web console and run sudo wg-quick down wg0. On a WireGuard server, 0.0.0.0/0 belongs in the client's config, and the server's [Peer] gets the client's tunnel address, such as 10.66.66.2/32. On a VPS that should route its own traffic through the tunnel, add PostUp = ip -4 rule add from <VPS address> lookup main priority 1000 with a matching PreDown. Test each change behind a timer that takes the tunnel down again.
What wg-quick Does With 0.0.0.0/0
wg-quick turns every peer's AllowedIPs into routes. The wg-quick man page adds that if one of them is a default route (0.0.0.0/0 or ::/0), "then it uses ip-rule(8) to handle overriding of the default gateway." For an IPv4 default, the script that Ubuntu 24.04 and Debian 12 ship runs these commands, plus a sysctl setting and a few firewall rules:
wg set wg0 fwmark 51820gives WireGuard's own encrypted UDP packets the firewall mark 51820 (0xca6c in hex).ip -4 route add 0.0.0.0/0 dev wg0 table 51820puts a default route through the tunnel in a separate routing table. If that table already has routes, wg-quick takes the next empty number for both.ip -4 rule add not fwmark 51820 table 51820sends every packet without the mark to that table.ip -4 rule add table main suppress_prefixlength 0lets packets use the normal main table first, but rejects any result with a prefix length of 0, which is the old default route.
The WireGuard routing page describes this setup and says it "is the technique used by the wg-quick(8) tool." After wg-quick up, ip rule shows it:
0: from all lookup local
32764: from all lookup main suppress_prefixlength 0
32765: not from all fwmark 0xca6c lookup 51820
32766: from all lookup main
32767: from all lookup default
Follow one SSH reply. You connect from 198.51.100.7 to the VPS at 203.0.113.10, so the VPS answers from 203.0.113.10 to 198.51.100.7. Rule 32764 finds only the default route in main and rejects it, so rule 32765 sends the unmarked reply to table 51820 and into wg0. The other end decrypts it and checks the source: per the WireGuard homepage, a packet whose source IP is in that peer's allowed IPs is "allowed onto the interface; otherwise it's dropped." Your peer's entry there normally holds your tunnel address, not your VPS's public address, so the reply dies.
Only the address families you list are affected: 0.0.0.0/0 changes IPv4 routing, and IPv6 changes only when ::/0 is listed too.
Step 1: Get Back In Through the Console
Your provider's web console, often called a VNC or serial console, works without the VPS's network. Log in there and take the tunnel down:
sudo wg-quick down wg0
If systemd started the tunnel (systemctl is-active wg-quick@wg0 prints active), stop and disable the service instead, so the next reboot doesn't lock you out again. The systemctl manual says --now with disable also stops the unit:
sudo systemctl disable --now wg-quick@wg0
Either way, SSH answers again once the interface and its rules are gone.
The console asks for a password. If you only log in with an SSH key, your account may not have one, so set one before your next test with sudo passwd "$USER". Without a console login, these get you back:
- A reboot from the control panel, if the tunnel does not start at boot. The routes and rules from
wg-quick uplive only in memory. - The provider's rescue system, if it has one. Mount the VPS disk and rename
/etc/wireguard/wg0.confon it towg0.conf.off; at the next boot wg-quick stops with a "does not exist" error before touching routes. - SSH over IPv6, if the config lists only
0.0.0.0/0and your home connection has IPv6. A Space-Node VPS has both IPv4 and IPv6, sossh -6 you@2001:db8::10, with your server's IPv6 address, still reaches it.
If none of that works, ask your host. Space-Node support answers by live chat, Discord and email from 08:00 to 20:00 UTC.
Step 2: Check Which Side Has 0.0.0.0/0
Look at the AllowedIPs lines in the config:
sudo grep -i allowedips /etc/wireguard/wg0.conf
The VPS is the WireGuard server, and your laptop or phone connects to it. Then 0.0.0.0/0 belongs in the client's file, where it sends the laptop's traffic through the VPS. AllowedIPs works in both directions: wg(8) describes it as the addresses "from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed." On the server, each [Peer] lists only that client's tunnel address:
[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.66.66.2/32
Without a /0 entry the server gets no fwmark rules, so that one line is the fix. Our WireGuard setup guide shows a server file and a client file.
The VPS is the client: it connects to a VPN provider, your home router or another server. If it only needs a private network behind the peer, go to step 4; if its own internet traffic should use the tunnel, go to step 5. Either way, arm the rollback from step 3 first.
Step 3: Arm a Timed Rollback Before Each Test
systemd-run can create a timer that runs a command later, outside your SSH session. Schedule the way back before you bring the tunnel up:
echo $SSH_CONNECTION
sudo systemd-run --collect --on-active=5min --unit=wg-rollback wg-quick down wg0
sudo wg-quick up wg0
Per ssh(1), $SSH_CONNECTION holds your client's IP address and port, then the server's IP address and port; note the first and third values. systemd-run replies Running timer as unit: wg-rollback.timer. --collect unloads the unit even if wg-quick down fails, so the name stays free for your next test.
Now open a new SSH connection from your computer. If it logs in, cancel the rollback:
sudo systemctl stop wg-rollback.timer
If it hangs, wait: after five minutes the timer runs wg-quick down wg0 and you can log in again. Once a fresh login works with the tunnel up, let systemd start it at boot:
sudo wg-quick down wg0
sudo systemctl enable --now wg-quick@wg0
Step 4: Route Only What Needs the Tunnel
If the VPS only needs to reach machines behind the peer, such as your home network, list those networks instead of everything:
[Peer]
PublicKey = <peer public key>
Endpoint = home.example.net:51820
AllowedIPs = 10.66.66.1/32, 192.168.1.0/24
Without a /0 entry, wg-quick adds ordinary routes such as ip -4 route add 192.168.1.0/24 dev wg0 to the main table, with no fwmark and no rules. Everything else, your SSH replies included, keeps using the default route. ip route show dev wg0 lists what goes through the tunnel.
Step 5: Keep the Full Tunnel, Answer Outside It
When the VPS should send its own traffic through the tunnel, keep 0.0.0.0/0 and add one rule: packets that leave with the VPS's own address as their source use the main table. Those are the replies from programs on the VPS itself, such as sshd or nginx, to connections that arrived on that address. Connections the VPS opens itself get the tunnel address as their source and still go through the tunnel.
Docker containers with published ports are the exception: Docker forwards those connections with destination NAT (Docker docs), and the kernel puts your public address back on a reply only in POST_ROUTING, after the route is chosen (nf_nat.h), so their replies don't match. A second rule for the container network, such as from 172.18.0.0/16, covers them but also takes the containers' own outgoing traffic out of the tunnel.
Find the address with ip -brief address show (it matches the third value of $SSH_CONNECTION) and add the rule to /etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <VPS private key>
Address = 10.66.66.2/32
PostUp = ip -4 rule add from 203.0.113.10 lookup main priority 1000
PreDown = ip -4 rule del from 203.0.113.10 lookup main priority 1000
[Peer]
PublicKey = <peer public key>
Endpoint = vpn.example.net:51820
AllowedIPs = 0.0.0.0/0
ip-rule(8) processes rules "in order of increasing number", so 1000 is checked before wg-quick's 32764 and 32765. It has to be PostUp, not PreUp. wg-quick adds its rules without a number, and the kernel then gives a new rule the number just below the first rule after 0: from all lookup local. If your rule already exists at that point, wg-quick's rules land at 998 and 999, in front of yours, and SSH breaks again.
If AllowedIPs also contains ::/0, add the same two lines with ip -6 and the address from ip -6 -brief address show scope global. Bring the tunnel up behind the timer from step 3, then check with the two addresses from $SSH_CONNECTION:
ip rule
ip route get 198.51.100.7 from 203.0.113.10
Rule 1000 should be listed above 32764, and the route should name your normal gateway and interface; dev wg0 table 51820 means replies still go into the tunnel. On the console, the same check works while SSH is frozen.
Step 6: Use Table = off or a Table Number
The Table setting in wg-quick(8) has two special values: off creates no routes at all, and auto, the default, adds routes with the default-route handling above. With Table = off, wg-quick leaves your routing alone, which makes it a good first test of the tunnel itself:
[Interface]
PrivateKey = <VPS private key>
Address = 10.66.66.2/32
Table = off
[Peer]
PublicKey = <peer public key>
Endpoint = vpn.example.net:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
When the interface comes up, WireGuard sends a keepalive to every peer that has PersistentKeepalive set (device.c), and with no session yet, that starts a handshake (send.c). sudo wg show wg0 then shows a latest handshake line if both ends can reach each other. No such line points to the keys, the endpoint or the UDP port, not routing.
With a number such as Table = 1234, wg-quick puts the routes, 0.0.0.0/0 included, in that table without the fwmark handling, and nothing uses the table until your own ip rule sends traffic there. The man page's example, PostUp = ip rule add ipproto tcp dport 22 table 1234, sends only outgoing SSH through the tunnel; selectors such as from and uidrange in ip-rule(8) pick an address or a user instead.
If It Still Fails
RTNETLINK answers: File existsduringwg-quick up. A rule from an earlier attempt is still there. wg-quick runs withset -e, so the failing PostUp stops it and it removes the interface. Run your PreDown command by hand, then try again.- A kill switch in the config. PostUp lines with
-j REJECT, like the kill-switch example in the wg-quick man page, reject packets that leave outside the tunnel without WireGuard's mark. That includes your SSH replies, even with the step 5 rule. On a server you manage over SSH, remove those lines. - Your edits disappear. With
SaveConfig = true, wg-quick saves the running configuration on shutdown, and the man page warns that changes made to the file before the interface is removed "will therefore be overwritten." Runsudo wg-quick down wg0first, then edit. - UFW was turned on during the setup. Check that
sudo ufw statuslists your SSH port, and add it withsudo ufw allow 22/tcp. Our Fail2Ban and UFW guide has the safe order. - It fails only after a reboot.
sudo journalctl -u wg-quick@wg0 -bshows each command wg-quick ran at boot, prefixed with[#], and the error that stopped it.
FAQ
Why did my open SSH session freeze, not only new logins? Whenever a rule changes, the kernel flushes its IPv4 route cache (fib_rules.c), so the next packet of your existing connection is routed with wg-quick's rules and goes into the tunnel.
Can I exclude my home IP from AllowedIPs instead?
You can list every range except your own address, but that protects only that one address, and a new IP from your internet provider locks you out again. Without a /0 entry wg-quick sets no fwmark, so traffic to the peer's endpoint must also stay outside the tunnel, as the WireGuard routing page explains for such setups. The step 5 rule works from any address.
Does wg-quick down undo everything?
It removes the interface, the routing rules it added and its firewall rules. Your own PostUp rules go away only through matching PreDown lines.
Will a reboot fix it?
Only if the tunnel does not start at boot. systemctl is-enabled wg-quick@wg0 prints enabled when it does; then run sudo systemctl disable --now wg-quick@wg0 from the console.
Sources
All opened on 31 October 2026:
- WireGuard: wg-quick(8) and its Ubuntu 24.04 copy, wg(8), wireguard-tools v1.0.20210914 wg-quick script, wg-quick@.service and show.c, Routing and network namespace integration, cryptokey routing
- Packages: wireguard-tools in Ubuntu 24.04, wireguard 1.0.20210914-1 in Debian 12
- Linux kernel: net/core/fib_rules.c, net/ipv4/fib_rules.c, WireGuard device.c and send.c, netfilter nf_nat.h and nf_nat_core.c
- Docker: Docker with iptables
- iproute2: ip-rule(8), ip-route(8), ip(8)
- systemd: systemd-run(1), run.c in v255 and v252, systemd.time(7), systemctl(1), journalctl(1), systemd.exec(5)
- OpenSSH, accounts and firewall: ssh(1), passwd(1), ufw(8)