Security

Set Up a WireGuard VPN on Your VPS in 10 Minutes

WireGuard is a modern VPN that fits in a few hundred lines of code, uses fixed cryptographic primitives, and behaves like a normal network interface. On a VPS with a public IPv4 address, you can stand up a working tunnel in about ten minutes: install the package, generate keys, write two short config files, enable forwarding and NAT, then add a client. This guide walks through each step with runnable commands. Every command below assumes a Debian or Ubuntu server and a Linux or mobile client, and assumes you have root or sudo access.

Why WireGuard, and what "in-kernel" means in 2026

WireGuard has been part of the mainline Linux kernel since version 5.6, released in March 2020. Any server running a modern distribution already has the kernel module available, so there is nothing to compile and no out-of-tree module to maintain. The wireguard package you install is mostly the userspace tooling: the wg command for key management and inspection, and wg-quick, a wrapper that brings an interface up or down from a single config file.

The practical consequence is speed and simplicity. The data path runs in the kernel, configuration lives in one file per interface, and the whole thing is managed through systemd like any other service.

Step 1: Install WireGuard

Install the tooling and, for the client QR code later, qrencode:

sudo apt update
sudo apt install -y wireguard qrencode

Confirm the module and tools are present:

wg --version
sudo modprobe wireguard && echo "kernel module ok"

On Red Hat family systems the equivalent is sudo dnf install wireguard-tools qrencode. The rest of this guide is identical once the tools are installed.

Step 2: Generate server keys

WireGuard uses a Curve25519 keypair per peer. Generate the server keys with a tight umask so the private key is never world-readable:

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

The command prints the server's public key to your terminal and writes both keys to /etc/wireguard. The umask 077 ensures the files are created mode 600, readable only by root. Treat the private key like an SSH host key: it never leaves the server.

Step 3: Write the server config

Create /etc/wireguard/wg0.conf. The interface name in the filename (wg0) is what wg-quick and the systemd unit reference later.

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>

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

A few notes on the fields:

  • Address is the server's address inside the VPN subnet. Here the tunnel uses 10.8.0.0/24, a private range that will not collide with typical LANs.
  • ListenPort 51820 is the WireGuard default. It runs over UDP.
  • PrivateKey is the contents of server_private.key. Paste the actual value, not the path.
  • PostUp / PostDown add and remove the NAT and forwarding rules when the interface goes up or down. Replace eth0 with your server's real outbound interface, which you can find with ip route show default.

A VPS with a dedicated IPv4 address makes the cleanest VPN endpoint, since clients connect to a stable public address and outbound NAT is straightforward.

Step 4: Enable IP forwarding

For the server to route client traffic to the internet, the kernel must forward packets between interfaces. Enable it persistently:

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

If your clients will use IPv6 through the tunnel, also add net.ipv6.conf.all.forwarding = 1 to the same file. The PostUp masquerade rule from Step 3 handles the address translation once forwarding is on.

Step 5: Start the tunnel and open the firewall

Bring the interface up and enable it on boot through the systemd template unit:

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

wg show should list the wg0 interface with its public key and listening port. Now allow the WireGuard UDP port through your firewall. With UFW:

sudo ufw allow 51820/udp
sudo ufw reload

If your VPS provider has a separate network-level firewall or security group, open UDP 51820 there as well. Keep your SSH port open so you do not lock yourself out.

Step 6: Add a client peer

Generate a keypair for the client. You can do this on the client machine, or on the server if you will hand the config over securely.

umask 077
wg genkey | tee client_private.key | wg pubkey > client_public.key

Register the client with the server by adding a [Peer] block to /etc/wireguard/wg0.conf:

[Peer]
PublicKey  = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.8.0.2/32

On the server, AllowedIPs is a routing statement: it says traffic for 10.8.0.2 belongs to this peer. Give each client a unique address in the subnet. Apply the change without dropping existing peers:

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

Now write the client config, for example client.conf:

[Interface]
Address = 10.8.0.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>
DNS = 1.1.1.1

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <SERVER_PUBLIC_IP>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

On the client, AllowedIPs = 0.0.0.0/0 routes all traffic through the tunnel (a full VPN). To tunnel only the VPN subnet instead, use AllowedIPs = 10.8.0.0/24. PersistentKeepalive = 25 keeps the session alive through NAT and stateful firewalls by sending a small packet every 25 seconds.

Connect from a Linux client

Copy client.conf to /etc/wireguard/wg0.conf on the client, then:

sudo wg-quick up wg0
ping -c3 10.8.0.1
curl -s https://ifconfig.co

The ping confirms the tunnel; the curl should return your server's public IP, confirming traffic is egressing through the VPS.

Connect from a phone with a QR code

The official WireGuard apps for Android and iOS can import a config by scanning a QR code. Render the client config as a QR code in your terminal:

qrencode -t ansiutf8 < client.conf

Open the WireGuard app, choose to add a tunnel by scanning, and point the camera at the terminal. Delete the on-screen QR code afterward, since it contains the client private key.

Verify and wrap up

Run sudo wg show on the server. A connected peer shows a latest handshake timestamp and non-zero transfer counters. If the handshake never appears, check the three usual suspects in order:

  1. The UDP port is open on both the host firewall and any provider security group.
  2. The client Endpoint uses the server's real public IP and the correct port.
  3. Public and private keys are not swapped between the two configs.

That is a complete WireGuard tunnel: kernel-level data path, two short config files, NAT and forwarding on the server, and clients added by appending a peer block. For the authoritative reference and protocol details, see the WireGuard Quick Start and the WireGuard project site. From here you can add more peers by repeating Step 6, or lock down AllowedIPs to build a split-tunnel instead of a full VPN.