How to Set Up a WireGuard VPN Server: Secure Remote Access to Your Home Network

Why WireGuard?

There’s a quiet moment that every self-hoster eventually faces: you’re sitting in a coffee shop, and you need to reach your home server. Maybe it’s a file share, maybe it’s your home automation dashboard, maybe you just want to check that your backups ran last night. You could expose services to the internet and hope your security holds. Or you could do the smart thing — tunnel in through a VPN.

WireGuard is the modern answer to VPN complexity. Where OpenVPN and IPsec feel like configuring a small country’s border infrastructure, WireGuard is almost absurdly simple: a few lines of config, a pair of keys, and you’re done. It’s fast enough to run on a Raspberry Pi, secure enough that Linus Torvalds approved of it, and simple enough that you can set it up in under 30 minutes.

In this tutorial, we’ll set up a WireGuard server on a Linux machine (Ubuntu/Debian), configure a client, and make sure it starts automatically on boot. By the end, you’ll have encrypted access to your home network from anywhere.

Prerequisites

  • A Linux server with root access (Ubuntu 20.04+ or Debian 11+ recommended)
  • Port 51820/UDP forwarded on your router to this server
  • A client device (Linux, macOS, Windows, iOS, or Android — WireGuard has apps for all of them)
  • Basic comfort with the command line

Step 1: Install WireGuard

sudo apt update
sudo apt install wireguard -y

That’s it. The wireguard-tools package gives you the wg and wg-quick utilities. On newer kernels (5.6+), WireGuard is built into the kernel itself, so you don’t even need a separate module.

Step 2: Generate Server Keys

WireGuard uses public-key cryptography. Each endpoint has a private key (never shared) and a public key (shared with peers). Let’s generate the server’s keypair:

cd /etc/wireguard
umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key

The umask 077 ensures the private key file is only readable by root. Don’t skip this — your private key should stay private.

Let’s grab the keys into variables for convenience:

SERVER_PRIVATE_KEY=$(cat server_private.key)
SERVER_PUBLIC_KEY=$(cat server_public.key)
echo "Server public key: $SERVER_PUBLIC_KEY"

Step 3: Create the Server Configuration

cat > /etc/wireguard/wg0.conf <<EOF
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = ${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

# Client configurations will go below this line
EOF

Let’s break down what’s happening here:

  • Address: The server gets IP 10.0.0.1 on the virtual wg0 interface. All clients will be in the 10.0.0.0/24 subnet.
  • ListenPort: Standard WireGuard port. Make sure your router forwards UDP 51820 here.
  • PostUp/PostDown: These iptables rules enable NAT, so clients can reach your home network and the internet through the server. Replace eth0 with your actual network interface (check with ip route | grep default).

Step 4: Enable IP Forwarding

Linux won’t forward packets between interfaces by default. Let’s enable it:

echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Step 5: Start WireGuard

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

Verify it’s running:

sudo wg show

You should see your interface listed with the server’s public key and no peers yet. That’s expected — we haven’t added any clients.

Step 6: Generate Client Keys

For each device that needs to connect, generate a keypair:

wg genkey | tee client_private.key | wg pubkey > client_public.key
CLIENT_PRIVATE=$(cat client_private.key)
CLIENT_PUBLIC=$(cat client_public.key)

Step 7: Add the Client to the Server Config

Append the client as a [Peer] section to the server config:

cat >> /etc/wireguard/wg0.conf <<EOF

[Peer]
PublicKey = ${CLIENT_PUBLIC}
AllowedIPs = 10.0.0.2/32
EOF

AllowedIPs with /32 means this client is assigned 10.0.0.2. For the next client, use 10.0.0.3/32, and so on.

Reload the server config without dropping existing connections:

sudo wg syncconf wg0 <(wg-quick strip wg0)

Step 8: Create the Client Configuration

Now create the config file your client device will use:

cat > client.conf <<EOF
[Interface]
PrivateKey = ${CLIENT_PRIVATE}
Address = 10.0.0.2/24
DNS = 1.1.1.1, 8.8.8.8

[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
Endpoint = YOUR_SERVER_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF

A few notes on the client config:

  • AllowedIPs = 0.0.0.0/0 on the client means all traffic goes through the VPN. If you only want access to your home network, use your LAN subnet instead (e.g., 192.168.1.0/24).
  • PersistentKeepalive = 25 keeps NAT mappings alive. Essential if the client is behind a home router or mobile network.
  • DNS prevents DNS leaks by explicitly using trusted resolvers through the tunnel.

Step 9: Connect from Your Client

Linux/macOS: Install wireguard tools and run sudo wg-quick up ./client.conf

Windows: Install the WireGuard app, import the .conf file, and click “Activate”.

iOS/Android: Install WireGuard from the app store, and either import the config file or scan the QR code (generate one with qrencode -t ansiutf8 < client.conf).

Step 10: Verify the Connection

Once connected, test from your client:

# Should show the WireGuard interface with a latest handshake and transfer
wg

# Should reach your home network devices
ping 10.0.0.1

# Should show your server's IP, not your real one
curl https://ifconfig.me

Common Pitfalls & Troubleshooting

No handshake appearing?

Check that port 51820/UDP is open on both your router and server firewall: sudo ufw allow 51820/udp. Also verify the public keys match — a typo here means the handshake will never complete.

Handshake works but no internet?

Check IP forwarding (cat /proc/sys/net/ipv4/ip_forward should return 1) and verify the PostUp iptables rules are correct. Make sure eth0 in the config matches your actual interface name.

DNS leaks?

If your DNS requests are going outside the tunnel, explicitly set DNS in the client config. On Linux, you can verify with dig +short whoami.akamai.net @ns1-1.akamaitech.net — it should return your VPN server’s IP.

Want to access the whole home network?

Change the client’s AllowedIPs to your LAN subnet (e.g., 192.168.1.0/24) instead of 0.0.0.0/0. This way only traffic destined for your home network goes through the tunnel, and your regular internet stays fast.

Wrapping Up

You now have a fully functional WireGuard VPN server. It’s fast, secure, and — compared to the alternatives — remarkably simple. The entire server config is maybe 15 lines. That’s the beauty of WireGuard: it does one thing, does it well, and stays out of your way.

A few parting tips:

  • Back up your keys from /etc/wireguard/ — losing the private key means regenerating everything.
  • Use QR codes for mobile clients. It’s much easier than copying config files around.
  • Consider PiVPN (pivpn.io) if you want an even more automated setup with a nice CLI for adding/removing clients.
  • Rotate keys periodically — generate new keypairs every few months for sensitive deployments.

Happy tunneling! 🔐

Leave a Reply

Your email address will not be published. Required fields are marked *