Kennisbank › Netwerken › Setting Up a WireGuard VPN on Your VPS
netwerken gevorderd 17 September 2026

Setting Up a WireGuard VPN on Your VPS

Setting Up a WireGuard VPN on Your VPS WireGuard turns a cheap VPS into a private VPN endpoint in about fifteen minutes — this article walks you through the

```bash sudo apt update sudo apt install -y wireguard wireguard-tools ```

On Ubuntu 22.04 and 24.04 the kernel module is already present; the `wireguard` package pulls in `wireguard-dkms` only if your kernel needs it. Check that the module loads cleanly:

```bash sudo modprobe wireguard lsmod | grep wireguard ```

If `lsmod` shows nothing, your kernel does not have the module and you are on the DKMS path. That is a real constraint, not a cosmetic one — DKMS rebuilds the module on every kernel upgrade, and when that rebuild fails (usually after an unattended-upgrades run), your tunnel silently stops coming back after a reboot.

Step 2 — Enable IP forwarding

Your VPS now needs permission to route packets between the tunnel interface and the public internet. Without this, clients connect to the server but get no internet through it.

```bash echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf sudo sysctl --system ```

The `99-` prefix matters. Files in `/etc/sysctl.d/` are read in lexical order, and a later file wins. Naming yours `99-` means it overrides anything a cloud image dropped in earlier.

If you only need IPv4, drop the second line. Leaving IPv6 forwarding on when you have no IPv6 route just means clients will try and fail to reach IPv6 destinations — a slow, confusing failure mode.

Step 3 — Generate the server keypair

WireGuard authenticates with raw Curve25519 keys. No certificate authority, no expiry, no revocation list. That simplicity is the feature and also the trap: a leaked private key is valid forever until you replace it manually.

```bash cd /etc/wireguard umask 077 wg genkey | sudo tee server.key | wg pubkey | sudo tee server.pub ```

`umask 077` makes the files readable only by root. Skip it and `wg genkey` writes world-readable keys — WireGuard's own `wg-quick` will refuse to start on a config file with loose permissions, which is a helpful failure but an annoying one to debug at 1 a.m.

Note the public key; you will paste it into every client config:

```bash sudo cat /etc/wireguard/server.pub ```

Step 4 — Write the server configuration

Create `/etc/wireguard/wg0.conf`. The interface name `wg0` is arbitrary but conventional; the filename must match the interface name for `wg-quick` to find it.

```bash sudo nano /etc/wireguard/wg0.conf ```

```ini [Interface] Address = 10.0.0.1/24 ListenPort = 51820 PrivateKey =

NAT for clients

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT PostUp = iptables -A FORWARD -o wg0 -j ACCEPT PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT PostDown = iptables -D FORWARD -o wg0 -j ACCEPT PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE ```

Replace `` with the actual private key. Replace `eth0` with your real outbound interface — check with `ip route get 1.1.1.1`. Getting this wrong is the single most common cause of "the tunnel is up but nothing loads."

Port 51820 is the WireGuard convention. Changing it does not improve security in any meaningful way; if you want to reduce noise in your logs, that is a valid reason, but it is not a substitute for a firewall rule.

The `PostUp` and `PostDown` lines are iptables rules. They are evaluated by `wg-quick` at interface start and stop. They are not persistent across a reboot on their own — the systemd unit handles that, which is the next step.

Step 5 — Start the tunnel and enable it at boot

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

`wg-quick up` reads the config, creates the interface, applies the routes, and runs the `PostUp` commands. `systemctl enable` registers the service so it comes back after a reboot.

Verify the interface came up:

```bash sudo wg show ```

You should see the interface name, the listening port, and the public key. No peers yet — that is expected.

Step 6 — Generate a client keypair

Do this on the client machine, not the server. The private key should never travel over the network.

On Linux or macOS:

```bash wg genkey | tee client.key | wg pubkey | tee client.pub ```

On Windows, use the WireGuard app's "Add Tunnel" → "Add empty tunnel" — it generates the keypair in the GUI and shows you the public key.

Copy the client's public key. You need it on the server.

Step 7 — Add the peer on the server

Back on the VPS, add the client as a peer. The `AllowedIPs` on the server side is the address the client will use inside the tunnel.

```bash sudo wg set wg0 peer allowed-ips 10.0.0.2/32 ```

Then persist it so it survives a restart. `wg set` is runtime-only; `wg-quick` reads the config file on boot and would otherwise forget the peer.

```bash sudo wg-quick save wg0 ```

`wg-quick save` rewrites `wg0.conf` with the current peer list. It is a blunt tool — it reorders and reformats the file — but it is the supported way to persist peers without editing by hand.

Assign each client a unique `/32`. Two clients sharing `10.0.0.2/32` will fight, and the symptom is intermittent, which is the worst kind of symptom.

Step 8 — Write the client configuration

On the client, create a config file. The `AllowedIPs` here is the opposite of the server's: it controls which traffic the client routes into the tunnel.

```ini [Interface] PrivateKey = Address = 10.0.0.2/32 DNS = 1.1.1.1

[Peer] PublicKey = Endpoint = :51820 AllowedIPs = 0.0.0.0/0, ::/0 PersistentKeepalive = 25 ```

`AllowedIPs = 0.0.0.0/0` routes everything through the tunnel — a full-tunnel VPN. If you only want to reach the VPS's private network, set it to `10.0.0.0/24` instead. Full tunnel is what most people mean by "VPN," but it also means all your traffic now depends on one VPS staying up.

`PersistentKeepalive = 25` sends a packet every 25 seconds so NAT on the client's side does not expire the UDP mapping. Without it, clients behind home routers lose the tunnel after a few idle minutes and only recover when they send traffic — which they cannot, because the mapping is gone.

Import the file into the WireGuard client and toggle it on.

Verification

On the server, check the handshake:

```bash sudo wg show ```

You want to see a `latest handshake` line with a timestamp less than two minutes old, and `transfer` counters that increment. A peer listed with no handshake means the client's packets are not reaching the server — check the firewall and the endpoint IP.

From the client, confirm the exit IP is your VPS:

```bash curl -s https://ifconfig.me ```

That should print the VPS's public IP, not your home IP. If it prints your home IP, the tunnel is up but not routing — revisit `AllowedIPs` and the `PostUp` NAT rules.

Common Problems

Handshake succeeds, no internet. The NAT rule is wrong. Run `ip route get 1.1.1.1` on the VPS, confirm the interface name, and fix the `MASQUERADE` line. This is almost always the cause.

Tunnel drops after a few minutes idle. `PersistentKeepalive` is missing or set too high on the client. 25 seconds is the standard value and works behind most carrier-grade NAT.

`wg-quick up` fails with "Permission denied" on the config. The file permissions are too open. `sudo chmod 600 /etc/wireguard/wg0.conf` and retry. WireGuard refuses to read a config that anyone but root can modify, because that config contains your private key.

The whole setup is a keypair, a config file, and two iptables rules. What it is not is a managed service — when the tunnel breaks, there is no dashboard and no support ticket. There is `wg show` and your own logs. That trade is the reason it runs at line speed on a two-dollar VPS, and it is also the reason you should write down what you changed before you change it.

← Terug naar kennisbank
← Terug naar kennisbank