A WireGuard full tunnel routes a client’s IPv4 internet traffic through the VPN server instead of sending only private-network traffic through the tunnel. In this tutorial, you will install WireGuard on Ubuntu 24.04, configure a Raff Technologies Linux VM as the internet gateway, enable forwarding and masquerading, route the client with AllowedIPs = 0.0.0.0/0, configure DNS for the tunnel, and verify the public egress path end to end.
Raff Linux VMs provide the public IPv4 address, Ubuntu environment, and root-level networking control needed for this pattern. The saved test environment for this tutorial remains a Raff Ubuntu 24.04 VM with 1 vCPU and 2 GB RAM. The original full-tunnel workflow was tested there; the routing, DNS, firewall, and key-handling guidance was re-verified against current Ubuntu Server and WireGuard documentation on September 6, 2026 without claiming a new machine test.
Ubuntu’s current WireGuard default-gateway documentation describes the same core design used here: the client sends 0.0.0.0/0 through WireGuard, while the gateway server enables IPv4 forwarding and masquerades the VPN subnet onto its public interface.
IPv4 scope: This tutorial creates an IPv4 full tunnel.
AllowedIPs = 0.0.0.0/0does not cover IPv6. If the client has working IPv6, configure an IPv6 WireGuard path separately or disable that path before claiming all network traffic is tunneled.
Full tunnel vs private-access WireGuard
| Design | Client AllowedIPs | Server forwarding/NAT | Result |
|---|---|---|---|
| Full tunnel — this tutorial | 0.0.0.0/0 | Required | Client IPv4 internet traffic exits through the VPN server |
| Private/split tunnel | VPN/private subnet only | Not required when accessing only the WireGuard server | Normal internet traffic stays on the client’s local connection |
If you only need private SSH or service access, use Set Up WireGuard on Ubuntu 24.04 for Private Access instead. Keeping the two designs separate avoids adding forwarding and NAT when they are not needed.
Prerequisites:
- An Ubuntu 24.04 server with a public IPv4 address
- SSH and sudo access with a working recovery path
- A Linux, macOS, Windows, Android, or iOS WireGuard client
- UDP port
51820reachable through the firewall protecting the VM - A VPN subnet that does not overlap the client or server networks
- A DNS resolver you want the client to use while the tunnel is active
This tutorial uses:
| Item | Value |
|---|---|
| Server WireGuard interface | wg0 |
| VPN subnet | 10.90.90.0/24 |
| Server tunnel IP | 10.90.90.1/24 |
| First client tunnel IP | 10.90.90.2/32 |
| WireGuard listener | 51820/udp |
| Example DNS resolver | 1.1.1.1 |
Step 1 — Verify Ubuntu, the public interface, and the VPN subnet
Confirm the operating system and current IPv4 routes:
cat /etc/os-release ip -4 addr ip -4 route
Find the interface Ubuntu uses for its default IPv4 route:
OUT_IFACE="$(ip route show default | awk '/default/ {print $5; exit}')" printf 'Outbound interface: %s\n' "$OUT_IFACE"
Typical public-interface names include eth0, ens3, and similar names. Do not hardcode eth0 before checking the real VM.
Verify that the proposed WireGuard subnet does not already exist on the server:
ip -4 route | grep '10\.90\.90\.0/24' || true
Run an equivalent route check on the client. If either side already uses 10.90.90.0/24, choose another RFC1918 subnet and replace it consistently throughout the tutorial.
Verify: Ubuntu should report 24.04, OUT_IFACE should resolve to the real public/default-route interface, and the proposed VPN subnet should not overlap an existing route.
Step 2 — Install WireGuard on Ubuntu 24.04
Install WireGuard and the utilities used for the gateway rules, firewall check, and verification:
sudo apt update sudo apt install -y wireguard iptables ufw curl
Verify the userspace tools:
wg --version wg-quick --help 2>&1 | head
Load the kernel module as a diagnostic check:
sudo modprobe wireguard lsmod | grep '^wireguard' || true
Ubuntu’s standard kernel includes WireGuard support, while the wireguard package provides tools such as wg and wg-quick.
Verify: wg --version and wg-quick should run successfully before you create keys or interfaces.
Step 3 — Generate the server key pair without embedding the private key in the config
Create the WireGuard directory and generate a protected private key:
sudo install -d -m 700 /etc/wireguard sudo sh -c 'umask 077; wg genkey > /etc/wireguard/wg0.key' sudo sh -c 'wg pubkey < /etc/wireguard/wg0.key > /etc/wireguard/wg0.pub'
Set explicit ownership and permissions:
sudo chown root:root /etc/wireguard/wg0.key /etc/wireguard/wg0.pub sudo chmod 600 /etc/wireguard/wg0.key sudo chmod 644 /etc/wireguard/wg0.pub
Display only the public key:
sudo cat /etc/wireguard/wg0.pub
Check file permissions without exposing the secret:
sudo stat -c '%a %U:%G %n' \ /etc/wireguard/wg0.key \ /etc/wireguard/wg0.pub
The private key remains in /etc/wireguard/wg0.key. The server configuration will load it at interface startup rather than storing the private key directly inside text you may later inspect while troubleshooting.
Verify: /etc/wireguard/wg0.key should be owned by root and mode 600. Never paste or screenshot that private key.
Step 4 — Enable IPv4 forwarding on the VPN gateway
A full-tunnel server must forward packets from the WireGuard interface to the public network interface.
Create a dedicated sysctl file:
sudo tee /etc/sysctl.d/70-wireguard-routing.conf > /dev/null <<'EOF' net.ipv4.ip_forward = 1 EOF
Apply it immediately:
sudo sysctl -p /etc/sysctl.d/70-wireguard-routing.conf
Verify the runtime value:
sysctl net.ipv4.ip_forward
Expected output:
net.ipv4.ip_forward = 1
Forwarding only allows the server to route packets. The next step adds the source-NAT rule required for the client’s private 10.90.90.0/24 address to reach the public internet through the server.
Verify: net.ipv4.ip_forward must equal 1 before the server can act as an IPv4 gateway.
Step 5 — Configure the WireGuard server with forwarding and scoped masquerading
Detect the public interface again:
OUT_IFACE="$(ip route show default | awk '/default/ {print $5; exit}')" printf '%s\n' "$OUT_IFACE"
Create /etc/wireguard/wg0.conf:
sudo tee /etc/wireguard/wg0.conf > /dev/null <<EOF [Interface] Address = 10.90.90.1/24 ListenPort = 51820 SaveConfig = false PostUp = wg set %i private-key /etc/wireguard/%i.key PostUp = iptables -I FORWARD 1 -i %i -o ${OUT_IFACE} -s 10.90.90.0/24 -j ACCEPT PostUp = iptables -I FORWARD 1 -i ${OUT_IFACE} -o %i -d 10.90.90.0/24 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT PostUp = iptables -t nat -A POSTROUTING -s 10.90.90.0/24 -o ${OUT_IFACE} -j MASQUERADE PostDown = iptables -D FORWARD -i %i -o ${OUT_IFACE} -s 10.90.90.0/24 -j ACCEPT PostDown = iptables -D FORWARD -i ${OUT_IFACE} -o %i -d 10.90.90.0/24 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT PostDown = iptables -t nat -D POSTROUTING -s 10.90.90.0/24 -o ${OUT_IFACE} -j MASQUERADE EOF
Protect the configuration:
sudo chown root:root /etc/wireguard/wg0.conf sudo chmod 600 /etc/wireguard/wg0.conf
Inspect the non-secret configuration:
sudo cat /etc/wireguard/wg0.conf
The NAT rule is restricted to 10.90.90.0/24; it does not masquerade unrelated VM traffic. The forwarding rules also restrict accepted forwarding to this WireGuard subnet.
Ubuntu’s default-gateway guide uses the same underlying model: enable forwarding and masquerade traffic from the WireGuard network onto the gateway’s public interface.
Verify: The config should use the detected public interface, load /etc/wireguard/wg0.key, and scope forwarding/NAT to 10.90.90.0/24.
Step 6 — Allow UDP 51820 without blindly enabling UFW over SSH
Check UFW before changing firewall state:
sudo ufw status numbered
If UFW is already active, allow the WireGuard listener:
sudo ufw allow 51820/udp comment 'WireGuard full tunnel' sudo ufw status numbered
If UFW is inactive, do not blindly enable it from one remote SSH session. First verify the real SSH port is allowed and keep a second SSH session or web-console recovery path available. Follow Set Up UFW Firewall on Ubuntu 24.04 for the lockout-safe activation sequence.
If another firewall protects the VM outside Ubuntu, permit UDP 51820 there too. This WireGuard listener does not need TCP port 51820.
Verify: UDP 51820 should be reachable through every firewall layer, while your existing SSH administration path remains working.
Step 7 — Generate the client key pair on the client device
Generate the client private key on the client whenever possible so it never has to pass through the server.
On an Ubuntu or Debian client:
sudo apt update sudo apt install -y wireguard
Create the client keys:
sudo install -d -m 700 /etc/wireguard sudo sh -c 'umask 077; wg genkey > /etc/wireguard/wg0.key' sudo sh -c 'wg pubkey < /etc/wireguard/wg0.key > /etc/wireguard/wg0.pub' sudo chmod 600 /etc/wireguard/wg0.key sudo chmod 644 /etc/wireguard/wg0.pub
Display only the client public key:
sudo cat /etc/wireguard/wg0.pub
Windows, macOS, Android, and iOS users can generate the peer keys in the official WireGuard application and use the same addresses and peer values shown below.
Verify: Only the client public key should be copied to the server. The client private key remains protected on the client device.
Step 8 — Add the client peer to the server
On the server, paste the client public key into a temporary shell variable:
CLIENT_PUBLIC_KEY='PASTE_CLIENT_PUBLIC_KEY_HERE'
Append the peer block:
sudo tee -a /etc/wireguard/wg0.conf > /dev/null <<EOF [Peer] PublicKey = ${CLIENT_PUBLIC_KEY} AllowedIPs = 10.90.90.2/32 EOF unset CLIENT_PUBLIC_KEY
Inspect the saved peer:
sudo grep -A2 '^\[Peer\]' /etc/wireguard/wg0.conf
The server-side AllowedIPs = 10.90.90.2/32 assigns that tunnel address to this peer. Do not reuse the same /32 for another client.
Verify: Replace the placeholder with the real client public key before starting WireGuard, and ensure this peer uniquely owns 10.90.90.2/32.
Step 9 — Start the WireGuard gateway and verify NAT
Enable and start the interface:
sudo systemctl enable --now wg-quick@wg0
Check service state, address, and listener:
systemctl is-active wg-quick@wg0 systemctl is-enabled wg-quick@wg0 ip -brief addr show wg0 sudo wg show wg0 sudo ss -lun | grep ':51820' || true
Verify the forwarding and NAT rules created by PostUp:
sudo iptables -S FORWARD | grep '10.90.90.0/24' sudo iptables -t nat -S POSTROUTING | grep '10.90.90.0/24'
Expected state includes:
wg0 has 10.90.90.1/24 UDP 51820 is listening forwarding rules reference 10.90.90.0/24 POSTROUTING contains MASQUERADE for 10.90.90.0/24
A latest handshake will not appear until the client sends traffic.
Verify: The service should be active/enabled and both forwarding plus masquerading rules should be present before the client connects.
Step 10 — Configure the Linux client as a full-tunnel peer
On the server, display the server public key:
sudo cat /etc/wireguard/wg0.pub
Get the server public IPv4 address from the Raff dashboard. You can also compare it with:
curl -4s https://icanhazip.com
On the Linux client, confirm systemd-resolved is available:
systemctl is-active systemd-resolved
Create /etc/wireguard/wg0.conf, replacing SERVER_PUBLIC_KEY and SERVER_PUBLIC_IP:
sudo tee /etc/wireguard/wg0.conf > /dev/null <<'EOF' [Interface] Address = 10.90.90.2/24 PostUp = wg set %i private-key /etc/wireguard/%i.key PostUp = resolvectl dns %i 1.1.1.1; resolvectl domain %i '~.' PostDown = resolvectl revert %i [Peer] PublicKey = SERVER_PUBLIC_KEY Endpoint = SERVER_PUBLIC_IP:51820 AllowedIPs = 0.0.0.0/0 PersistentKeepalive = 25 EOF sudo chmod 600 /etc/wireguard/wg0.conf
Replace both placeholders before continuing.
AllowedIPs = 0.0.0.0/0 tells wg-quick to send all IPv4 destinations through this peer. On Linux, wg-quick implements the default-route behavior with policy routing so the WireGuard endpoint itself remains reachable outside the tunnel.
The resolvectl commands make 1.1.1.1 the DNS resolver for the WireGuard link and set ~. so DNS queries prefer that link while the full tunnel is active. Replace 1.1.1.1 with another resolver if your policy requires one.
PersistentKeepalive = 25 is useful when the client sits behind NAT or a stateful firewall and needs to keep its mapping alive. WireGuard documents 25 seconds as a sensible interval for that case; omit it when you do not need persistent reachability.
On Windows, macOS, Android, or iOS, use the official WireGuard application and set the peer to AllowedIPs = 0.0.0.0/0. Use the application’s DNS field for your selected resolver instead of the Linux resolvectl lines.
Verify: The client configuration should have 10.90.90.2/24, the real server endpoint, 0.0.0.0/0, and a deliberate DNS path for the full tunnel.
Step 11 — Bring up the client and verify the full-tunnel route
Start the Linux client interface:
sudo wg-quick up wg0
Inspect its address and peer state:
ip -brief addr show wg0 sudo wg show wg0
Inspect the policy-routing rules that wg-quick created:
ip rule list ip route list table 51820 2>/dev/null || true
A typical full-tunnel setup shows a dedicated policy-routing table containing a default route through wg0. If table 51820 is already in use on the client, wg-quick can choose another table number; the ip rule output reveals the active policy.
Inspect DNS routing on the WireGuard link:
resolvectl status wg0
Expected state includes the configured resolver and a route-only domain of ~..
Verify: wg0 should be up with 10.90.90.2/24; WireGuard policy routing should contain the IPv4 default route; and DNS should be assigned to the WireGuard link.
Step 12 — Verify the full-tunnel VPN end to end
From the client, reach the server’s tunnel address:
ping -c 4 10.90.90.1
Check the client’s public IPv4 address:
curl -4s https://icanhazip.com
The result should match the VPN server’s public IPv4 address.
Inspect the WireGuard handshake and transfer counters on the client:
sudo wg show wg0
On the server, inspect the peer as well:
sudo wg show wg0
A working peer should report a recent handshake and increasing transfer counters.
Verify DNS still resolves while the tunnel is active:
resolvectl query example.com getent hosts example.com
If the client has IPv6 connectivity, test that path separately:
curl -6 --connect-timeout 5 https://icanhazip.com \ || echo 'No working IPv6 path'
If the IPv6 request succeeds, that traffic is outside this IPv4-only tunnel. Do not describe the setup as an all-protocol tunnel until IPv6 is handled explicitly.
