Maybe more. And for what? A company you've never met, running servers you can't see, promising they don't log your traffic — in a privacy policy that runs to 4,000 words.
Here's what they don't advertise: you can build something better yourself. A VPN you own completely, running on hardware you control, that costs nothing if you already have a spare Linux machine at home.
The tool is called **WireGuard**. It's been built into the Linux kernel since 2020. Faster than OpenVPN, simpler than IPsec, and you can have it fully running in about 15 minutes.
Let's do it.
Before you start — understand the setup
This tutorial is written for the most common home scenario:
- You have **one Linux machine at home** — a desktop, old laptop, or Raspberry Pi that stays on - You want your **phone and other laptops** to tunnel all their internet traffic through it when you're out on public Wi-Fi or travelling
This means: - Your Linux machine is the **server** — you do almost everything on it - Your phone and other devices are **clients** — they connect to the server - You never install a client config on the Linux machine itself. It only runs the server
The only moment you pick up your phone during this tutorial is to scan a QR code at the very end.
What you need
- A Linux machine (Ubuntu/Debian used in this tutorial) - Your phone with the WireGuard app installed (free on App Store and Google Play) - About 15 minutes
Step 1 — Install WireGuard
```bash sudo apt update && sudo apt install wireguard qrencode -y ```
We're installing `qrencode` at the same time — you'll need it later to generate the QR code for your phone.
— -
Step 2 — Move into the WireGuard directory
All config files live in `/etc/wireguard/`. This directory is locked down to root, so move into it using sudo:
```bash sudo -i cd /etc/wireguard ```
> **Note:** `sudo -i` gives you a root shell. Everything from here until told otherwise runs from inside `/etc/wireguard` as root. This avoids the permission denied errors you'd get running individual commands as a regular user.
Verify you're in the right place:
```bash pwd # Should output: /etc/wireguard ```
— -
Step 3 — Generate the server keys
Still inside `/etc/wireguard` as root:
```bash wg genkey | tee server_private.key | wg pubkey > server_public.key ```
Lock down the private key so only root can read it:
```bash chmod 600 server_private.key ```
Verify both files were created and have content:
```bash ls -la cat server_private.key # shows a base64 string cat server_public.key # shows a different base64 string ```
Each should show a string that looks like `oMNlMUKMBUaFSHBvVMgqzN+3bkSQr7Is2gHk…` — keep this terminal open, you'll need these values shortly.
— -
Step 4 — Find your network interface name
WireGuard needs to know what your machine calls its main network adapter. Run:
```bash ip route | grep default ```
You'll see output like one of these:
``` default via 192.168.1.1 dev eth0 proto static default via 192.168.1.1 dev ens3 proto static default via 192.168.1.1 dev enp0s3 proto static ```
The interface name is the word **immediately after `dev`** — in the examples above that's `eth0`, `ens3`, or `enp0s3`. Note yours down. You'll need it in the next step.
— -
Step 5 — Create the server config
Still inside `/etc/wireguard` as root, create the main config file:
```bash nano wg0.conf ```
Paste this in, making two substitutions: 1. Replace `PASTE_SERVER_PRIVATE_KEY_HERE` with the contents of `server_private.key` 2. Replace `eth0` in the `PostUp` and `PostDown` lines with your actual interface name from Step 4
```ini [Interface] # The VPN's internal IP address for this server Address = 10.0.0.1/24
# The port WireGuard listens on ListenPort = 51820
# Your server's private key PrivateKey = PASTE_SERVER_PRIVATE_KEY_HERE
# These lines allow your devices to reach the internet through this server # IMPORTANT: Replace eth0 with your interface name from Step 4 PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE ```
Save and exit: `Ctrl+X`, then `Y`, then `Enter`.
> **If your interface is `ens3`**, those two lines would end with `-o ens3 -j MASQUERADE` instead. Wrong interface name = devices connect but have no internet. This is the most common mistake.
— -
Step 6 — Enable IP forwarding
Without this your server will receive packets but refuse to pass them on to the internet:
```bash sysctl -w net.ipv4.ip_forward=1 echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf ```
The first line enables it immediately. The second makes it survive reboots.
— -
Step 7 — Open the firewall port
```bash ufw allow 51820/udp ufw reload ```
Verify the rule is in:
```bash ufw status | grep 51820 ```
You should see `51820/udp` listed as `ALLOW`.
— -
Step 8 — Start WireGuard
```bash wg-quick up wg0 ```
Enable it to start automatically every time the machine boots:
```bash systemctl enable wg-quick@wg0 ```
Verify it's running:
```bash wg show ```
You should see your `wg0` interface listed with your public key and listening port. The server is ready. Now let's add your phone.
— -
Step 9 — Add your phone
Everything in this step still runs on the **server**, inside `/etc/wireguard` as root.
### Generate phone keys
```bash wg genkey | tee phone_private.key | wg pubkey > phone_public.key ```
Verify they were created:
```bash cat phone_private.key # base64 string cat phone_public.key # different base64 string ```
Create the phone config file
This command automatically pulls your real public IP and both key files — no manual substitution needed:
```bash bash -c 'cat > /tmp/phone.conf << EOF [Interface] Address = 10.0.0.2/32 PrivateKey = $(cat /etc/wireguard/phone_private.key) DNS = 1.1.1.1
[Peer] PublicKey = $(cat /etc/wireguard/server_public.key) Endpoint = $(curl -s ifconfig.me):51820 AllowedIPs = 0.0.0.0/0, ::/0 PersistentKeepalive = 25 EOF' ```
Before going any further, verify the config looks right — especially that `Endpoint` shows a real IP address, not a placeholder:
```bash cat /tmp/phone.conf ```
The `Endpoint` line should look like `Endpoint = 82.45.12.33:51820` with your actual public IP. If it still says `YOUR_SERVER_IP` or is blank, something went wrong — don't proceed until this shows a real IP.
Register the phone on the server
Open `wg0.conf` and add the phone as a peer at the bottom:
```bash nano wg0.conf ```
Add these lines at the very end of the file (leave everything above untouched):
```ini [Peer] # Phone PublicKey = PASTE_PHONE_PUBLIC_KEY_HERE AllowedIPs = 10.0.0.2/32 ```
Replace `PASTE_PHONE_PUBLIC_KEY_HERE` with the contents of `phone_public.key`:
```bash cat phone_public.key ```
Save and exit: `Ctrl+X`, then `Y`, then `Enter`.
Reload WireGuard without restarting it
```bash wg syncconf wg0 <(wg-quick strip wg0) ```
This applies your config changes instantly without dropping anything. No downtime.
Generate the QR code
```bash qrencode -t ansiutf8 < /tmp/phone.conf ```
A QR code appears right in your terminal.
Connect your phone
1. Open the WireGuard app on your phone 2. Tap the **+** button 3. Tap **Scan from QR code** 4. Point your camera at the terminal screen 5. Give the tunnel a name (e.g. "Home VPN") 6. Tap the toggle to connect
To verify it's working, open a browser on your phone and go to **whatismyip.com** — you should see your home server's public IP, not your phone's mobile IP.
Clean up the temp file
```bash rm /tmp/phone.conf ```
— -
Step 10 — Verify everything on the server
```bash wg show ```
You should see your phone listed as a peer with a recent handshake:
``` interface: wg0 public key: J9Tlrn2OoqZa8sHe9sVmn… private key: (hidden) listening port: 51820
peer: TpVQ3mZK8A… (your phone) endpoint: 82.45.12.99:52011 allowed ips: 10.0.0.2/32 latest handshake: 23 seconds ago transfer: 1.23 MiB received, 3.67 MiB sent ```
"Latest handshake" a few seconds or minutes ago means the connection is live and healthy.
— -
Adding more devices later
Every additional device — another phone, a laptop, a tablet — follows the same pattern. Each one gets its own unique IP address (`10.0.0.3`, `10.0.0.4`, and so on).
On your **server** (as root in `/etc/wireguard`):
```bash # 1. Generate keys for the new device wg genkey | tee device_private.key | wg pubkey > device_public.key
# 2. Create the config (change Address IP and filename each time) bash -c 'cat > /tmp/device.conf << EOF [Interface] Address = 10.0.0.3/32 PrivateKey = $(cat /etc/wireguard/device_private.key) DNS = 1.1.1.1
[Peer] PublicKey = $(cat /etc/wireguard/server_public.key) Endpoint = $(curl -s ifconfig.me):51820 AllowedIPs = 0.0.0.0/0, ::/0 PersistentKeepalive = 25 EOF'
# 3. Verify the config — check Endpoint shows a real IP cat /tmp/device.conf
# 4. Add the device as a peer in wg0.conf nano wg0.conf # Add at the bottom: # [Peer] # PublicKey = (contents of device_public.key) # AllowedIPs = 10.0.0.3/32
# 5. Reload WireGuard wg syncconf wg0 <(wg-quick strip wg0)
# 6. Show QR code (for phones) or copy the config file (for laptops) qrencode -t ansiutf8 < /tmp/device.conf
# 7. Clean up rm /tmp/device.conf ```
For a **laptop**, instead of scanning a QR code: install WireGuard on the laptop, copy the contents of `/tmp/device.conf` into `/etc/wireguard/wg0.conf` on that laptop, then run `sudo wg-quick up wg0` on the laptop to connect.
— -
Useful commands to know
| What | Command (run on server as root) | | — -| — -| | Check VPN status + connected devices | `wg show` | | Start VPN | `wg-quick up wg0` | | Stop VPN | `wg-quick down wg0` | | Reload config without restarting | `wg syncconf wg0 <(wg-quick strip wg0)` | | Check your server's public IP | `curl ifconfig.me` | | Check VPN service status | `systemctl status wg-quick@wg0` |
— -
## What you just built
You have a WireGuard VPN running on hardware you own that:
- Encrypts all traffic from your phone using ChaCha20 — the same cipher in TLS 1.3 - Routes everything through **your own machine**, not a stranger's server - Keeps no logs because you configured it and nothing is logging anything - Costs **$0/month** — you're using a machine you already own - Has no device limit, no bandwidth cap, no subscription - Takes about 2 minutes to add each new device - Starts automatically every time your machine boots
The only maintenance it ever needs is the occasional `sudo apt upgrade` on the server. WireGuard is part of the Linux kernel now — it's not going anywhere.
That's what "free, forever" actually looks like.
*If you get stuck, drop a comment with the exact error message and which step you're on — these setups are very reproducible and almost every issue comes down to one line.*
*Follow for more tools that make your computer do things it was always capable of.*