2024-12-01 • 4 min

How to Create a VPN with WireGuard

This guide will show you how to configure a VPN with WireGuard on a Linux server.

Configuration Directory

Create the /etc/wireguard directory and set up the permissions so only the root user can access its contents:

mkdir -p /etc/wireguard
chown -R root:root /etc/wireguard
chmod -R 700 /etc/wireguard

It’s a good idea to also set the mask so new files are created with the correct permissions by default:

umask 077

Note: files won’t be created with the execute permission set by default, even with the mask.

Directory structure

There is no right or wrong way to struture the configuration directoy, but this is how I like to do it. Don’t worry about the key files for now, we’ll create them in the next section.

/etc/wireguard
├── clients                     // Client config files
│   ├── client1.conf
│   └── client2.conf
├── keys                        // Client and server keys
│   ├── client1
│   │   ├── client1             // Private key
│   │   ├── client1.pub         // Public key
│   │   └── server-client1.psk  // Pre-shared key
│   ├── client2
│   │   ├── client2
│   │   ├── client2.pub
│   │   └── server-client2.psk
│   └── server
│       ├── server
│       └── server.pub
└── wg0.conf                    // Server config file

I like to keep a copy of the clients’ config files and private keys in the server, but it’s not necessary. It might even be a horrible idea, depending on your circumstances.

Keys

You’ll need to create a key pair for each host in your VPN, including the server:

wg genkey > server               # Private key
wg pubkey < server > server.pub  # Public key

Additionally, for each client, you’ll need to create a pre-shared key that both the client and server must know:

wg genpsk > server-client1.psk

As one would expect, the server must also know all of the clients’ public keys. Each client must also know the server’s public key. Whether to keep a copy of each client’s private key stored in /etc/wireguard (as shown in the example above) is up to you.

Configuration Files

WireGuard can be configured through plain text files, which contain an [Interface] block and one or multiple [Peer] blocks.

Server

The server has a [Peer] block for every client and redirects VPN traffic to its local network:

[Interface]
Address = 172.19.0.1/16
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>

# VPN to Local Network Redirection
# Substitute "eth0" with the internet-facing interface
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

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

[Peer]
# client1
PublicKey = <CLIENT1_PUBLIC_KEY>
PresharedKey = <SERVER_CLIENT1_PRESHARED_KEY>
AllowedIPs = 172.19.0.2/32

[Peer]
# client2
PublicKey = <CLIENT2_PUBLIC_KEY>
PresharedKey = <SERVER_CLIENT2_PRESHARED_KEY>
AllowedIPs = 172.19.0.3/32

Client

Clients only need to have the server as a [Peer], since communication between clients goes through it.

[Interface]
Address = 172.19.0.2/16
ListenPort = 51820
PrivateKey = <CLIENT1_PRIVATE_KEY>
DNS = 192.168.218.1

[Peer]
# server
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <SERVER_CLIENT1_PRESHARED_KEY>
Endpoint = <SERVER_IP_OR_DOMAIN_NAME>:<LISTEN_PORT>
AllowedIPs = 0.0.0.0/0, ::/0

The optional DNS parameter in the [Interface] block can be used to make the router in the server’s local network handle DNS queries. This is useful if you want to access other hosts on the server’s local network using their hostnames instead of their IP addresses.

In this case the AllowedIPs parameter tunnels all traffic through the VPN. If you’re only using the VPN to access, say, your home’s LAN remotely and you don’t want your internet traffic going through your home’s router, change it to something like this:

AllowedIPs = 172.19.0.0/16, 192.168.218.0/24

Note: you can have multiple config files in the same client if to support both behaviors

An easy way to copy a configuration file to a client device is through a QR Code created with the qrencode utility:

qrencode -t ansiutf8 -r /etc/wireguard/clients/client1.conf

How to start the VPN

If your server config file is called wg0.conf, you can start or stop the VPN with wg-quick:

wg-quick up wg0
wg-quick down wg0

Systemd Service

If you want to start WireGuard automatically, you can also create a Systemd service:

systemctl enable --now [email protected]

Tips

Now you should have a working VPN, but here are some additional tips to improve the setup.

How to Reach VPN Clients from the Server’s Local Network

We’ve configured the server to redirect traffic so that other hosts in the same local network as the server are reachable through the VPN.

If you want those local hosts to also be able to reach VPN clients, create a routing rule in your router to redirect traffic directed to addresses in the VPN network through the VPN server.

If that’s not an option or you don’t want to make the routing rule at the router level, you can create it in Linux for a single host like this:

ip route add 172.19.0.0/16 via <VPN_SERVER_LOCAL_IP> dev <INTERFACE_NAME>

How to choose the VPN’s Network Address

Generally, you want to choose an uncommon network address to minimize the chances of overlap. For example, if you were to choose 192.168.0.0/24 you might find that a VPN client at 192.168.0.2 is unreachable if you’re connected to a LAN that has another host in the same address.

In the configuration examples, we’ve used 172.19.0.0/16, which should be “weird” enough to avoid overlap in most cases.

Firewall

Since the server’s WireGuard port is exposed to the internet, it’s a good idea to configure a firewall. Here’s an example UFW config to get you started:

ufw enable
ufw allow from 192.168.218.0/24
ufw allow from 172.19.0.0/16
ufw limit proto udp from any to any port 51820
Back to blog

© 2025 Diego Otero