WireGuard for One: Tunneling Your Mobile Device Through a Remote VPS

WireGuard for One: Tunneling Your Mobile Device Through a Remote VPS
We're not in Ohio anymore Toto

This isn’t hard. But if you’ve never done it, the moving parts might look scarier than they are.

Here’s how to stand up a WireGuard server on a remote VPS (like Linode), and connect a mobile device so it routes all traffic through that tunnel. No third-party VPN services, no accounts, no fluff. Just raw connectivity under your control.


Why You’d Do This

In my case, I needed to route my wife's phone traffic through a remote server so her bird feeder camera app would stop choking on local network detection. Tuya and others often hard-lock app behavior depending on whether the phone appears to be inside the local LAN. So we made it not look local.


The Plan

  • Set up a WireGuard server on an Ubuntu 24.04 VPS.
  • Generate both server and client keypairs on that VPS.
  • Serve a QR code of the client config for easy mobile import.
  • Tunnel everything from the phone through the VPS.

The Quick Route

If you just want to get the tunnel running fast, there’s a script at the end of this post that will set everything up automatically. It’s designed for a fresh VM dedicated to running a WireGuard tunnel, and assumes you’re starting from a clean slate.

It should also work on any Linux VM that has a public IP address—even if it's doing other things—but I’m not responsible for what might break if you run it on a production system. Use it on something clean, verify it works, and then adapt as needed.

1. Install WireGuard

On your VPS (Ubuntu 24.04 assumed):

sudo apt update
sudo apt install wireguard -y

2. Generate the Keypairs

Run all this on the VPS:

mkdir ~/wg-setup
cd ~/wg-setup

# Server keypair
wg genkey | tee server_private.key | wg pubkey > server_public.key

# Client (phone) keypair
wg genkey | tee phone_private.key | wg pubkey > phone_public.key

You’ll use these in the config files below.


3. Configure the WireGuard Server

Create the interface config:

sudo nano /etc/wireguard/wg0.conf

Paste the following (replace keys with the ones you just generated):

[Interface]
PrivateKey = <server_private_key>
Address = 10.66.66.1/24
ListenPort = 51820
SaveConfig = true

[Peer]
PublicKey = <phone_public_key>
AllowedIPs = 10.66.66.2/32

Save and close.


4. Enable IP Forwarding and NAT

First, enable forwarding:

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

Then add a NAT rule to let tunnel traffic exit via your VPS’s real interface:

sudo iptables -t nat -A POSTROUTING -s 10.66.66.0/24 -o eth0 -j MASQUERADE

5. Start WireGuard

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

6. Make Sure the Firewall Isn’t Screwing You

If your VPS provider (Linode, etc.) has a cloud firewall, make sure:

  • Port 51820/UDP is allowed inbound
  • All outbound traffic is allowed out (or at least UDP 53 and TCP 80/443)

If you’re also running ufw, allow traffic like so:

sudo ufw allow 51820/udp
sudo ufw allow OpenSSH

7. Build the Phone Config

Make a new file:

nano phone.conf

Paste:

[Interface]
PrivateKey = <phone_private_key>
Address = 10.66.66.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = <server_public_key>
Endpoint = <your.vps.ip.address>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Note: You’ll need to replace all three <...> placeholders with the actual key values from the files you generated and the public IP of your VPS.


But How Do We Get This Onto a Phone?

You’re not typing that private key by hand. And you’re probably not emailing it either.

Here’s the simple, portable, offline-friendly method:

Install QRencode

sudo apt install qrencode

Generate a QR Code

qrencode -o phone.png < phone.conf

Serve it Locally

Use Python’s built-in HTTP server (available on nearly every Linux distro):

python3 -m http.server 8080

Now, visit:

http://<your-vps-ip>:8080/phone.png

In WireGuard, tap on the + icon to add a new tunnel, and select the "Scan from QR code" option. Scan the code at the URL above. If all is good, WireGuard will ask you for a tunnel name. Name the tunnel, then connect to it.

You can now kill the web server:

CTRL+C

And optionally shred the key files if you're security-conscious:

shred phone_private.key
rm phone_private.key
rm phone.png

8. Done

Open the WireGuard app on the phone, connect the tunnel, and verify it’s working:

Visit https://ifconfig.me — you should see your VPS IP.


Bonus Notes

  • Want to limit what gets tunneled? Change AllowedIPs = 0.0.0.0/0 to just the ranges or domains you want routed.
  • Want to run your own DNS through the tunnel? Add a DNS forwarder like dnsmasq to the VPS and point the client at 10.66.66.1.

Summary

For anyone wondering: yes, you can build your own mobile VPN in about 15 minutes, with no third-party accounts or cloud-dependent garbage. It’s faster than commercial VPNs, completely under your control, and easy to nuke if needed.

And best of all: it doesn’t rely on some vendor’s definition of where your device “belongs.”

In my case, I built this tunnel to route a mobile app through my home internet connection, which is 1 Gbit down / 50 Mbit up. Devices connected through this WireGuard tunnel consistently pulled 230 Mbit/sec download over the tunnel — more than enough for HD video, app streams, or whatever weird encrypted camera feed you're dealing with.

The actual throughput you’ll get depends on both ends:

  • My Linode nanode (1-core, lowest tier) is limited to 40 Mbit down / 1 Mbit up
  • But because the traffic is mostly egress from my home lab, the performance holds up beautifully

Your results could be even better if you’re using a VPS with better bandwidth or regionally closer.

The Script (IPV4 Only)

This is a Bash script. Copy this code and paste it into a script named wg-phone-setup.sh on your VM. Then run it by typing bash wg-phone-setup.sh.


set -e

# Constants
WG_IF="wg0"
WG_PORT=51820
WG_SUBNET="10.66.66.0/24"
WG_SERVER_IP="10.66.66.1"
WG_PHONE_IP="10.66.66.2"
WG_DIR=~/wg-setup
QR_IMAGE="phone.png"

echo "[*] Installing required packages..."
sudo apt update
sudo apt install -y wireguard qrencode

echo "[*] Creating working directory..."
mkdir -p "$WG_DIR"
cd "$WG_DIR"

echo "[*] Generating server keys..."
wg genkey | tee server_private.key | wg pubkey > server_public.key

echo "[*] Generating phone keys..."
wg genkey | tee phone_private.key | wg pubkey > phone_public.key

SERVER_PRIV=$(<server_private.key)
SERVER_PUB=$(<server_public.key)
PHONE_PRIV=$(<phone_private.key)
PHONE_PUB=$(<phone_public.key)

# Auto-detect public IPv4 and default interface
VPS_IP=$(ip -4 route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}')
DEFAULT_IF=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++) if ($i=="dev") print $(i+1)}')

echo "[*] Detected public IP: $VPS_IP"
echo "[*] Detected default interface: $DEFAULT_IF"

echo "[*] Writing WireGuard server config..."
sudo tee /etc/wireguard/$WG_IF.conf > /dev/null <<EOF
[Interface]
PrivateKey = $SERVER_PRIV
Address = $WG_SERVER_IP/24
ListenPort = $WG_PORT
SaveConfig = true

[Peer]
PublicKey = $PHONE_PUB
AllowedIPs = $WG_PHONE_IP/32
EOF

echo "[*] Enabling IP forwarding..."
sudo tee /etc/sysctl.d/99-wireguard.conf > /dev/null <<EOF
net.ipv4.ip_forward=1
EOF
sudo sysctl --system

echo "[*] Adding iptables NAT rule..."
sudo iptables -t nat -A POSTROUTING -s $WG_SUBNET -o $DEFAULT_IF -j MASQUERADE

echo "[*] Enabling and starting WireGuard..."
sudo systemctl enable wg-quick@$WG_IF
sudo systemctl start wg-quick@$WG_IF

echo "[*] Writing phone config..."
tee phone.conf > /dev/null <<EOF
[Interface]
PrivateKey = $PHONE_PRIV
Address = $WG_PHONE_IP/32
DNS = 1.1.1.1

[Peer]
PublicKey = $SERVER_PUB
Endpoint = $VPS_IP:$WG_PORT
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF

echo "[*] Generating QR code..."
qrencode -o $QR_IMAGE < phone.conf

echo "[*] Serving QR code on Python web server..."
echo
echo "Scan this from your phone's WireGuard app:"
echo " → http://$VPS_IP:8080/$QR_IMAGE"
echo
echo "Press CTRL+C when you're done importing."
echo

python3 -m http.server 8080

Afterwards, run the commands below to clean up the keys:

shred ~/wg-setup/phone_private.key
rm ~/wg-setup/phone_private.key
rm ~/wg-setup/phone.png

--Sharing is caring
-Bryan