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

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 at10.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