Self-Host a Secure VPN on a Linux VPS with WireGuard
Engineering6 min read

Self-Host a Secure VPN on a Linux VPS with WireGuard

Build a fast, private VPN endpoint you control with WireGuard on a Linux VPS. This practical guide covers server hardening, routing, client setup, DNS, and the checks that prove traffic is really using the tunnel.

Self-Host a Secure VPN on a Linux VPS with WireGuard

A hosted VPN service is convenient, but running your own WireGuard endpoint gives you a fixed private gateway, control over logs and configuration, and a simple way to reach a trusted network while travelling. WireGuard is a small, modern VPN protocol built around public-key cryptography. On a modest Linux VPS it is fast, low-maintenance, and easier to audit than a sprawling VPN stack.

This guide builds a full-tunnel VPN: when the client is connected, its ordinary IPv4 traffic exits through the VPS. You can later narrow that to selected private networks if you only need remote access.

> Before you start: use a VPS provider and jurisdiction you trust. A self-hosted VPN protects the path between your device and the VPS, but it does not make you anonymous to the VPS provider or destination services.

What you need

  • A Linux VPS with a public IPv4 address (Ubuntu 24.04 LTS is used below)
  • Root or sudo access
  • A firewall rule allowing UDP on a chosen port (51820 below)
  • A client with the official WireGuard app or package

Use your own values in place of the examples:

| Value | Example | Purpose | | --- | --- | --- | | Server address | 203.0.113.10 | Public VPS IP or DNS name | | WireGuard port | 51820/udp | Encrypted tunnel listener | | VPN subnet | 10.66.66.0/24 | Private addresses inside the tunnel | | Server tunnel IP | 10.66.66.1 | Gateway address clients use |

1. Prepare and harden the VPS

Update the machine first, then install WireGuard and the firewall tooling. Keep an existing SSH session open while changing firewall rules so you can recover if a rule is wrong.

sudo apt update && sudo apt upgrade -y
sudo apt install -y wireguard ufw

# Allow administration first, then the VPN listener.
sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw enable
sudo ufw status verbose

If your VPS has a provider-side firewall, allow the same inbound UDP port there as well. UFW alone cannot override a cloud firewall.

2. Enable IPv4 forwarding

Your VPS must be allowed to route packets from the VPN interface out to the internet. Create a dedicated sysctl drop-in instead of modifying a broad system file.

sudo tee /etc/sysctl.d/99-wireguard-forwarding.conf >/dev/null <<'EOF'
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
sysctl net.ipv4.ip_forward

The last command should print net.ipv4.ip_forward = 1. If you also intend to tunnel IPv6, configure IPv6 forwarding, addresses, firewall policy, and DNS deliberately; do not advertise an IPv6 full tunnel until those pieces are ready.

3. Create the server keys and configuration

WireGuard peers authenticate with key pairs. Generate the server private key with restrictive permissions, then derive its public key. Never send a private key through chat, email, or a ticket.

sudo install -d -m 700 /etc/wireguard
cd /etc/wireguard
sudo sh -c 'umask 077; wg genkey | tee server.key | wg pubkey > server.pub'
sudo cat server.pub

Find the VPS's outbound network interface before adding NAT. It is commonly eth0, but do not guess:

ip route show default

Create /etc/wireguard/wg0.conf, replacing eth0 if your default route uses another interface. The PostUp and PostDown rules translate VPN-client addresses to the VPS public address for internet access.

[Interface]
Address = 10.66.66.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>

PostUp = ufw route allow in on wg0 out on eth0
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PostDown = ufw route delete allow in on wg0 out on eth0

Lock down the configuration after saving it:

sudo chmod 600 /etc/wireguard/wg0.conf

4. Add a client peer

Generate a separate key pair for every phone, laptop, or tablet. That lets you revoke one lost device without replacing every configuration. Generate the keys on the client when practical; the following commands generate a client key pair on the server for simplicity.

wg genkey | tee client1.key | wg pubkey > client1.pub

Append a peer stanza to the server configuration using the contents of client1.pub:

[Peer]
# Client: laptop-alex
PublicKey = <client1 public key>
AllowedIPs = 10.66.66.2/32

Start the service and inspect its state:

sudo systemctl enable --now wg-quick@wg0
sudo wg show
sudo systemctl status wg-quick@wg0 --no-pager

If the service fails, use sudo journalctl -u wg-quick@wg0 -b --no-pager rather than repeatedly restarting it. Configuration errors are usually visible there.

5. Import the client configuration

Create a client profile with the private key from client1.key, the server public key from /etc/wireguard/server.pub, and your real public address. The DNS setting sends DNS lookups through a resolver selected in the profile; choose a resolver appropriate to your privacy policy.

[Interface]
PrivateKey = <client1 private key>
Address = 10.66.66.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = <server public key>
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Import this into the WireGuard client app, or save it as a protected file and run:

sudo wg-quick up ./client1.conf

AllowedIPs = 0.0.0.0/0 makes this a full IPv4 tunnel. For split tunnelling, replace it with only the private network(s) you want to reach, such as 10.66.66.0/24 or an internal LAN range. PersistentKeepalive = 25 is useful for roaming clients behind NAT; it is not generally necessary for a permanently reachable server.

6. Verify the tunnel before relying on it

A connected-looking client is not enough. Confirm the handshake, endpoint, routing, and egress address.

On the server:

sudo wg show
sudo iptables -t nat -S POSTROUTING

Look for a recent handshake and non-zero received/transmitted bytes after you generate traffic from the client. On the connected client, compare the apparent public IP with the VPS address:

curl https://ifconfig.me

Then test DNS resolution and ordinary HTTPS access. If the handshake never appears, check the provider firewall, UFW, server endpoint/IP, and the two peer public keys. If a handshake appears but browsing fails, re-check IP forwarding, the outbound-interface name in NAT rules, and the client AllowedIPs.

Operating the VPN safely

Treat the VPS as a production internet host. Apply security updates, keep SSH key-only if feasible, remove peers for devices you no longer own, and back up configurations without storing private keys in a public repository. Check peer inventory periodically with sudo wg show.

For a compromised or lost client, remove that client's [Peer] block from wg0.conf, apply the change with sudo wg syncconf wg0 <(sudo wg-quick strip wg0), and create a fresh key pair when the device is ready again. Keep a record of which public key belongs to which device; public keys are safe identifiers, private keys are secrets.

WireGuard gives you a compact, dependable private gateway, but the real strength comes from operational discipline: minimal firewall exposure, unique peer keys, deliberate routing, and verification after every change.

Comments

Loading…

Read next

There were not enough close context matches, so these picks come from the rest of the published archive.

PublishedCloudPublished Apr 15

Mastering Kubernetes on Google Cloud: A Comprehensive Guide

Dive deep into deploying, managing, and scaling containerized applications using Kubernetes on Google Cloud Platform (GCP). This comprehensive guide covers GKE architecture, detailed setup, operational best practices, advanced deployment strategies, and real-world examples visualized with diagrams.

Read article →