Why WireGuard Changed Everything
I ran OpenVPN for years. It worked, most of the time, but the configuration was a maze of certificates, TLS keys, and config files that took an afternoon to set up and broke in ways that were never obvious. When I switched to WireGuard, the entire server config was 15 lines. The connection time dropped from several seconds to effectively instant. And it has been stable in a way that OpenVPN never was for me.
WireGuard is a VPN protocol built into the Linux kernel since version 5.6. It is not an application you install on top of the network stack — it is part of it. The entire codebase is roughly 4,000 lines of code, compared to OpenVPN’s 100,000+. Fewer lines means fewer bugs, a smaller attack surface, and performance that is noticeably faster. This is not marketing. It is measurably true.
From the field: I run WireGuard as my primary VPN for remote access to my homelab. After years of OpenVPN configs that felt like they needed a PhD to maintain, WireGuard’s simplicity was a revelation. A single config file, minimal overhead, and it just works.
This guide walks you through setting up a WireGuard VPN server on Ubuntu 24.04, adding clients for your phone and laptop, and understanding the difference between split tunnel and full tunnel configurations. I will also explain where Tailscale fits in and why learning raw WireGuard still matters even if you end up using Tailscale in practice.

Prerequisites
Before you start, you will need:
- An Ubuntu 24.04 server with a public IP address (a VPS, a cloud instance, or a home server with port forwarding configured on your router)
- sudo access on that server
- Port 51820/UDP open on your firewall and forwarded if behind NAT
- Basic command-line comfort. If you need a refresher, see Linux commands that actually get you hired
If your server is behind a home router, you will need to set up port forwarding for UDP 51820 to your server’s internal IP. The exact steps vary by router, but the concept is the same everywhere: tell the router to send incoming traffic on that port to your WireGuard server. If your ISP uses CGNAT (carrier-grade NAT), port forwarding will not work and you will need a VPS or a relay service like Tailscale instead.
Step 1: Install WireGuard
On Ubuntu 24.04, WireGuard is available in the default repositories. The kernel module is already included:
sudo apt update
sudo apt install -y wireguard
That is it. No PPAs, no third-party repos, no kernel module compilation. This is one of the advantages of using a modern Ubuntu LTS release — WireGuard support is baked in.
Verify the kernel module is available:
sudo modprobe wireguard
lsmod | grep wireguard
You should see wireguard in the output. If not, your kernel may be too old or the module is missing. On Ubuntu 24.04, this should not happen unless you are running a custom kernel.
Step 2: Generate Server Keys
WireGuard uses public/private key pairs, similar to SSH. Every peer (server and each client) needs its own pair.
# Create the config directory with restricted permissions
sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
# Generate the server's private and public keys
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key
# Lock down the private key
sudo chmod 600 /etc/wireguard/server_private.key
View the keys (you will need them in the next step):
sudo cat /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_public.key
The private key must never leave the server. Do not copy it into a client config, do not paste it in a chat, do not commit it to a repository. If the private key is compromised, anyone with it can impersonate your server. Treat it like an SSH private key.
Step 3: Configure the Server
Create the WireGuard interface configuration file:
sudo nano /etc/wireguard/wg0.conf
Add the following configuration:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = YOUR_SERVER_PRIVATE_KEY
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
SaveConfig = false
Replace YOUR_SERVER_PRIVATE_KEY with the output of sudo cat /etc/wireguard/server_private.key.
A few notes on this configuration:
- Address: The VPN subnet.
10.0.0.1/24gives you 254 possible clients, which is more than enough for most setups. The server takes.1. - ListenPort: 51820 is the default. There is no strong reason to change it, but you can if you want to.
- PostUp/PostDown: These iptables rules enable NAT so your VPN clients can reach the internet through the server. Replace
eth0with your server’s actual network interface name.
Find your network interface name:
ip route show default
The output will show something like default via 192.168.1.1 dev enp0s3. The name after dev is your interface. Common names are eth0, ens3, enp0s3, or ens18 (Proxmox VMs). Update the PostUp and PostDown lines accordingly.
Getting the interface name wrong is one of the most common WireGuard setup mistakes. If you use eth0 but your interface is actually ens3, the NAT rules will silently fail to apply. Your VPN will connect but clients will not be able to reach anything beyond the server itself. Always check with ip route show default.
Step 4: Enable IP Forwarding
By default, Linux does not forward packets between network interfaces. You need to enable this for VPN traffic to pass through the server:
# Enable immediately
sudo sysctl -w net.ipv4.ip_forward=1
# Make it persistent across reboots
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
If you also want IPv6 forwarding (and you should if your network uses it):
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
Step 5: Configure the Firewall
If you are running UFW (Ubuntu’s default firewall), allow WireGuard traffic:
# Allow WireGuard port
sudo ufw allow 51820/udp
# Allow SSH (so you don't lock yourself out)
sudo ufw allow OpenSSH
# Enable UFW if it isn't already
sudo ufw enable
# Verify
sudo ufw status
UFW also needs to allow forwarded traffic. Edit the UFW configuration:
sudo nano /etc/ufw/before.rules
Add these lines before the *filter section at the top of the file:
# NAT table rules for WireGuard
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
COMMIT
Again, replace eth0 with your actual interface name. Then update the default forward policy:
sudo nano /etc/default/ufw
Change DEFAULT_FORWARD_POLICY="DROP" to DEFAULT_FORWARD_POLICY="ACCEPT".
Restart UFW:
sudo ufw disable && sudo ufw enable
Step 6: Start WireGuard
# Start the WireGuard interface
sudo wg-quick up wg0
# Enable it to start on boot
sudo systemctl enable wg-quick@wg0
Verify it is running:
sudo wg show
You should see your interface listed with its public key and listening port, and zero peers (we have not added any yet).
Step 7: Generate Client Keys and Configuration
For each device you want to connect, generate a key pair and create a configuration. I will walk through adding a laptop and a phone.
Generate client keys
# Create a directory for client configs
mkdir -p ~/wireguard-clients
# Generate keys for the first client (e.g., laptop)
wg genkey | tee ~/wireguard-clients/laptop_private.key | wg pubkey > ~/wireguard-clients/laptop_public.key
# Generate keys for a second client (e.g., phone)
wg genkey | tee ~/wireguard-clients/phone_private.key | wg pubkey > ~/wireguard-clients/phone_public.key
Create the client configuration
Create a config file for the laptop client:
[Interface]
PrivateKey = LAPTOP_PRIVATE_KEY
Address = 10.0.0.2/32
DNS = 1.1.1.1, 8.8.8.8
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = your-server-public-ip:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Replace LAPTOP_PRIVATE_KEY with the contents of laptop_private.key, SERVER_PUBLIC_KEY with server_public.key, and the endpoint with your server’s public IP or domain.
The AllowedIPs line determines the tunnel type:
0.0.0.0/0— Full tunnel. ALL traffic goes through the VPN. Your public IP changes to the server’s. Good for privacy on untrusted networks (hotel Wi-Fi, coffee shops).10.0.0.0/24, 192.168.1.0/24— Split tunnel. Only traffic destined for the VPN subnet and your home network goes through the tunnel. Everything else uses the client’s normal internet connection. Better for daily use when you trust your local network and just need access to home services.
I run split tunnel for my daily setup. Full tunnel routes ALL your internet traffic through your home connection, which means your browsing speed is limited by your home upload speed. On a typical UK residential connection with 10-20 Mbps upload, that is noticeable. Split tunnel gives you the best of both worlds: access to home services plus full local internet speed.
Add the client as a peer on the server
Back on the server, add each client to the wg0.conf:
sudo nano /etc/wireguard/wg0.conf
Add a [Peer] section for each client at the bottom of the file:
[Peer]
# Laptop
PublicKey = LAPTOP_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
[Peer]
# Phone
PublicKey = PHONE_PUBLIC_KEY
AllowedIPs = 10.0.0.3/32
Restart WireGuard to apply:
sudo wg-quick down wg0 && sudo wg-quick up wg0
Step 8: Connect Your Devices
Laptop (Linux, macOS, Windows)
Install the WireGuard client for your OS from wireguard.com. Import the client configuration file you created. On Linux, you can copy the config to /etc/wireguard/wg0.conf and use wg-quick up wg0. On Windows and macOS, the WireGuard GUI lets you import the file directly.
Phone (iOS/Android)
Install the WireGuard app from the App Store or Google Play. The easiest way to get the config onto your phone is with a QR code. Install qrencode on your server:
sudo apt install -y qrencode
# Generate a QR code for the phone config
qrencode -t ansiutf8 < ~/wireguard-clients/phone.conf
A QR code will render in your terminal. Open the WireGuard app on your phone, tap "Add a tunnel," select "Create from QR code," and scan it. Done. The entire phone setup takes under a minute.
Step 9: Verify the Connection
After connecting a client, check the server:
sudo wg show
You should see the connected peer with a recent handshake timestamp and data transfer stats. If you see latest handshake: (none), the client has not successfully connected. Common causes:
- Port 51820 is not open or not forwarded correctly
- The endpoint IP or domain in the client config is wrong
- Key mismatch between server and client configs
- ISP blocking UDP traffic on that port (rare but possible)
From the connected client, test connectivity:
# Ping the server's VPN IP
ping 10.0.0.1
# If using full tunnel, check your public IP
curl ifconfig.me
If the full tunnel is working, curl ifconfig.me should return your server's public IP, not your local one.
What About Tailscale?
Tailscale builds on WireGuard and adds automatic key exchange, NAT traversal, and a management layer. It is genuinely excellent, and for many people it is the right choice. Install it on two devices, log in, and they can reach each other. No port forwarding, no manual key management, no firewall configuration.
So why bother with raw WireGuard?
Three reasons. First, understanding. Setting up WireGuard manually teaches you how VPNs actually work -- key exchange, routing, NAT, firewall rules. This knowledge transfers to every networking scenario you will encounter. Second, control. Raw WireGuard has no dependency on a third-party coordination server. Tailscale's control plane is excellent but it is a dependency. Third, flexibility. Raw WireGuard lets you build exactly the topology you want, whether that is a simple point-to-point tunnel, a hub-and-spoke setup, or a full mesh.
My honest advice: learn raw WireGuard first (you are doing that now), then decide whether Tailscale's convenience is worth the trade-offs for your use case. For a homelab with a few devices, raw WireGuard is perfectly manageable. For a fleet of devices across multiple locations with non-technical users, Tailscale earns its keep.
VPN configuration is a fundamental networking skill that appears in virtually every infrastructure role. Understanding WireGuard, IPsec, and VPN topology -- not just "install this app" but the actual key exchange, routing, and firewall mechanics -- separates infrastructure engineers from help desk. Enterprise environments use different tools (Cisco AnyConnect, Palo Alto GlobalProtect, Azure VPN Gateway) but the concepts are identical. Building a WireGuard server from scratch is the best networking lab exercise I can recommend.
Troubleshooting
VPN connects but cannot reach the internet
This is almost always a NAT or forwarding issue. Check that IP forwarding is enabled (cat /proc/sys/net/ipv4/ip_forward should return 1), verify the PostUp iptables rules are using the correct interface name, and ensure UFW's forward policy is set to ACCEPT.
Handshake never completes
The server is not receiving the client's packets. Check that port 51820/UDP is open: sudo ss -ulnp | grep 51820. If it is listening but the handshake still fails, the issue is between the client and the server -- a firewall, NAT, or ISP blocking the port. Test from outside your network.
DNS not resolving through the tunnel
If you can ping IPs through the VPN but domain names do not resolve, the DNS setting in your client config is either missing or pointing to a DNS server that is not reachable through the tunnel. Using public DNS servers (1.1.1.1, 8.8.8.8) in the client config is the simplest fix. If you run Pi-hole on your home network, point DNS to that instead.
Connection drops after a few minutes
Add PersistentKeepalive = 25 to the peer section in your client config. Without it, NAT tables on routers between the client and server can time out and drop the mapping. The keepalive sends a packet every 25 seconds to keep the NAT entry alive.
What to Do Next
With a VPN running, you can securely access your entire homelab from anywhere. Here is how to build on this foundation:
- Install Nextcloud with Docker -- now that you have a VPN, you can access your self-hosted cloud storage securely from anywhere without exposing it to the public internet.
- Install Proxmox VE -- build the virtualisation platform that hosts all your homelab services. WireGuard gives you remote access to manage it.
- Monitor Your VPN with Uptime Kuma -- set up a ping check against your WireGuard server so you know immediately if it goes down.
- Install Docker on Ubuntu 24.04 -- the foundation for running containerised services on your VPN-connected server.
- Self-Hosted Password Manager -- keep your passwords on your own infrastructure, accessible securely via WireGuard from any device.
A VPN is not just a privacy tool. It is the secure transport layer that makes self-hosting practical for daily use. Once it is running, every service on your home network becomes accessible from your phone on the train, your laptop at a coffee shop, or a hotel room on the other side of the country. That is real infrastructure independence.
Watch Out For This
If you are behind CGNAT (common with some ISPs), WireGuard will not work without a relay. Check with your ISP or use a VPS as a bounce point.
Key Takeaways
- WireGuard is built into the Linux kernel, making it faster and simpler than OpenVPN. The entire server config is roughly 15 lines.
- Always verify your network interface name with
ip route show defaultbefore writing the WireGuard config. Getting this wrong is the number one setup mistake. - IP forwarding must be enabled and persistent (
net.ipv4.ip_forward = 1in sysctl) or your VPN clients will connect but not reach anything beyond the server. - Use split tunnel (
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24) for daily use. Full tunnel routes everything through your home connection, which is limited by your upload speed. - Generate QR codes with
qrencodefor the fastest mobile client setup. Scanning a QR code is far easier than typing out keys on a phone keyboard. - Tailscale is built on WireGuard and removes the manual configuration. Learn raw WireGuard first for the understanding, then decide if Tailscale's convenience fits your use case.
- Add
PersistentKeepalive = 25to client configs if connections drop. NAT routers timeout idle UDP sessions.
Related Guides
If you found this useful, these guides continue the journey:
- How to Install Tailscale on Linux -- WireGuard without the manual config, ideal for getting started quickly
- How to Set Up DuckDNS for Free Dynamic DNS -- keep your homelab reachable when your ISP changes your IP
- How to Install Nginx Proxy Manager with Docker -- reverse proxy and HTTPS for services behind your VPN
- How to Build Your First Homelab in 2026 -- the full picture if you are just getting started
- What Is Zero Trust Network Access (ZTNA)? -- the next evolution beyond traditional VPNs

ReadTheManual is run, written and curated by Eric Lonsdale.
Eric has over 20 years of professional experience in IT infrastructure, cloud architecture, and cybersecurity, but started with PCs long before that.
He built his first machine from parts bought off tables at the local college campus, hoping they worked. He learned on BBC Micros and Atari units in the early 90s, and has built almost every PC he’s used between 1995 and now.
From helpdesk to infrastructure architect, Eric has worked across enterprise datacentres, Azure environments, and security operations. He’s managed teams, trained engineers, and spent two decades solving the problems this site teaches you to solve.
ReadTheManual exists because Eric believes the best way to learn IT is to build things, break things, and actually read the manual. Every guide on this site runs on infrastructure he owns and maintains.
Enjoyed this guide?
New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.

