..

How to tunnel all your outgoing traffic, but keep your connection to your VPS

Add this to your wireguard config:

[Interface]
# under interface add:
PostUp = /etc/wireguard/post-up.sh
PostDown = /etc/wireguard/post-down.sh

Fill in the variables and use these AI-written bash scripts, post up:

#!/bin/bash
#
# This script is executed by WireGuard's PostUp hook.
# It creates a new routing table and rules to ensure that traffic
# originating from the server's public IP is routed through the
# public gateway, preserving new and established connections.

# ==============================================================================
# Configuration - PLEASE FILL IN THESE VALUES
# ==============================================================================

# Your server's public IPv4 address.
# Example: PUBLIC_IPV4=""
PUBLIC_IPV4=""

# Your server's public IPv6 address (optional, leave blank if none).
# Example: PUBLIC_IPV6="2001:db8::1"
PUBLIC_IPV6=""

# Your server's public-facing network interface.
# Find with: ip a | grep 'state UP'
# Example: PUBLIC_INTERFACE="eth0"
PUBLIC_INTERFACE="eth0"

# The gateway for your public IPv4 address.
# Find with: ip route | grep default
# Example: GATEWAY_IPV4="198.51.100.1"
GATEWAY_IPV4=""

# The gateway for your public IPv6 address (optional, leave blank if none).
# Find with: ip -6 route | grep default
# Example: GATEWAY_IPV6="2001:db8::ffff"
GATEWAY_IPV6=""

# --- Advanced Configuration ---
# A unique ID for the custom routing table. Choose a number between 1 and 252
# that is not already in use in /etc/iproute2/rt_tables.
TABLE_ID=52
TABLE_NAME="keep_connections"
# The priority for the routing rule. Lower numbers are checked first.
PRIORITY=1000

# ==============================================================================
# Script Logic - DO NOT EDIT BELOW THIS LINE
# ==============================================================================

set -e # Exit immediately if a command exits with a non-zero status.

echo "--- [WG PostUp] Maintaining public connectivity ---"

# Add a named routing table for clarity. This makes it easier to identify its purpose.
if ! grep -q "^\s*$TABLE_ID\s*$TABLE_NAME\s*$" /etc/iproute2/rt_tables; then
    echo "Adding routing table '$TABLE_NAME' to /etc/iproute2/rt_tables"
    # Using tee with sudo to ensure we have permission to write to the file
    echo "$TABLE_ID $TABLE_NAME" | sudo tee -a /etc/iproute2/rt_tables > /dev/null
fi

# --- Add routes to the custom table ---
# These routes define how to send traffic out through the public interface.

# IPv4
if [[ -n "$PUBLIC_IPV4" && -n "$GATEWAY_IPV4" ]]; then
    echo "Adding IPv4 default route to table '$TABLE_NAME'..."
    ip route add default via "$GATEWAY_IPV4" dev "$PUBLIC_INTERFACE" table "$TABLE_ID"
else
    echo "Skipping IPv4 route configuration (address or gateway not set)."
fi

# IPv6
if [[ -n "$PUBLIC_IPV6" && -n "$GATEWAY_IPV6" ]]; then
    echo "Adding IPv6 default route to table '$TABLE_NAME'..."
    ip -6 route add default via "$GATEWAY_IPV6" dev "$PUBLIC_INTERFACE" table "$TABLE_ID"
else
    echo "Skipping IPv6 route configuration (address or gateway not set)."
fi

# --- Add policy routing rules ---
# These rules tell the kernel to use our custom table for any traffic coming *from* the public IP.

# IPv4
if [[ -n "$PUBLIC_IPV4" ]]; then
    if ! ip rule list | grep -q "from $PUBLIC_IPV4 lookup $TABLE_NAME"; then
        echo "Adding IPv4 policy rule..."
        ip rule add from "$PUBLIC_IPV4/32" table "$TABLE_ID" priority "$PRIORITY"
    else
        echo "IPv4 policy rule already exists."
    fi
fi

# IPv6
if [[ -n "$PUBLIC_IPV6" ]]; then
    if ! ip -6 rule list | grep -q "from $PUBLIC_IPV6 lookup $TABLE_NAME"; then
        echo "Adding IPv6 policy rule..."
        ip -6 rule add from "$PUBLIC_IPV6/128" table "$TABLE_ID" priority "$PRIORITY"
    else
        echo "IPv6 policy rule already exists."
    fi
fi

echo "--- [WG PostUp] Script finished successfully ---"

post down:

#!/bin/bash
#
# This script is executed by WireGuard's PostDown hook.
# It cleans up the rules and routes created by the PostUp script
# to restore the server's default routing behavior.

# ==============================================================================
# Configuration - MUST MATCH YOUR PostUp SCRIPT
# ==============================================================================

# Your server's public IPv4 address.
# Example: PUBLIC_IPV4="198.51.100.5"
PUBLIC_IPV4=""

# Your server's public IPv6 address (optional, leave blank if none).
# Example: PUBLIC_IPV6="2001:db8::1"
PUBLIC_IPV6=""

# --- Advanced Configuration ---
TABLE_ID=52
TABLE_NAME="keep_connections"
PRIORITY=1000

# ==============================================================================
# Script Logic - DO NOT EDIT BELOW THIS LINE
# ==============================================================================

set -e # Exit immediately if a command exits with a non-zero status.

echo "--- [WG PreDown] Cleaning up routing rules ---"

# --- Remove policy routing rules ---

# IPv4
if [[ -n "$PUBLIC_IPV4" ]]; then
    # Use a loop to be safe, in case a rule was added multiple times by mistake.
    while ip rule list | grep -q "from $PUBLIC_IPV4.*lookup $TABLE_NAME"; do
        echo "Removing IPv4 policy rule..."
        ip rule del from "$PUBLIC_IPV4/32" table "$TABLE_ID" priority "$PRIORITY"
    done
fi

# IPv6
if [[ -n "$PUBLIC_IPV6" ]]; then
    while ip -6 rule list | grep -q "from $PUBLIC_IPV6.*lookup $TABLE_NAME"; do
        echo "Removing IPv6 policy rule..."
        ip -6 rule del from "$PUBLIC_IPV6/128" table "$TABLE_ID" priority "$PRIORITY"
    done
fi

# --- Flush the custom routing table ---
# This removes the default routes we added to our custom table.
echo "Flushing routing table '$TABLE_NAME'..."
ip route flush table "$TABLE_ID"
if [[ -n "$PUBLIC_IPV6" ]]; then
    ip -6 route flush table "$TABLE_ID"
fi

echo "--- [WG PostDown] Script finished successfully ---"