WireGuard has quickly begun to replace long-established VPN protocols such as OpenVPN and IPsec in recent years. The reason is not marketing but an architectural choice: WireGuard uses modern cryptography (Curve25519, ChaCha20, Poly1305), its codebase is far smaller than classic VPN software, and because it runs at kernel level the performance cost is low. But that simplicity comes at a price — unlike OpenVPN, WireGuard offers no complex certificate authority or username/password system; everything comes down to two simple things: key pairs and a configuration file.

Generating a key pair

WireGuard authenticates with public/private key pairs; there are no usernames, passwords or certificates. Every endpoint (the server and each client) generates its own key pair and shares only its public key with the other side. The private key must never leave the disk or the device.

This command first generates a random private key (wg genkey), writes it to the privatekey file, and at the same time pipes it into the wg pubkey command to compute the corresponding public key and save it to the publickey file. You need to run this separately on the server and on every client device that will connect — each endpoint must have its own unique key pair.

The structure of the wg0.conf file

A WireGuard configuration file consists of two sections. The [Interface] section defines this device's own settings: its private key, its IP address inside the tunnel and an optional DNS server. The [Peer] section defines the other side to connect to: its public key, its address and which traffic will go through this tunnel.

There is an important nuance here: "Peer" is not an absolute role but a mutual concept. A client's config file contains a single [Peer] block (that block represents the server). But the config on the server side has its own [Interface] block and lists a separate [Peer] block for every connected client. So the only difference between a server and a client is how many Peer blocks you define.

Interface fields

FieldMeaning
<code>PrivateKey</code>This device's private key; never shared
<code>Address</code>This device's IP address inside the tunnel (e.g. <code>10.0.0.2/32</code>)
<code>DNS</code>Optional; the DNS server to use while the tunnel is active
<code>ListenPort</code>Usually specified only on the server side; the default WireGuard port is 51820/UDP

Peer fields

FieldMeaning
<code>PublicKey</code>The other side's (the peer's) public key
<code>Endpoint</code>The other side's address and port (e.g. <code>vpn.example.com:51820</code>); only needed on the side that is reachable from outside
<code>AllowedIPs</code>Which destination IPs the traffic going through this peer will be routed into the tunnel for
<code>PersistentKeepalive</code>The interval in seconds for keeping the connection alive while the client is behind NAT

An example client configuration

Once you have prepared this file, the steps for copying it to the server and enabling it are as follows:

AllowedIPs: full-tunnel or split-tunnel?

When AllowedIPs = 0.0.0.0/0, ::/0 is written, the entire IPv4 and IPv6 address space is covered; in practice this means "send all traffic over the VPN" — including your internet access (a full-tunnel VPN). If instead you write only the VPN's own subnet (for example 10.0.0.0/24), only traffic going to that subnet passes through the tunnel; everything else, including your general internet access, uses the normal network path. This is called split-tunnel and is usually preferred when you only want to reach internal resources (a fleet of servers, for example) and do not want to route all your internet traffic over the VPN.

Why PersistentKeepalive matters

PersistentKeepalive = 25 is the value WireGuard officially recommends when the client is behind NAT (almost all home/mobile connections). NAT devices silently delete the port mapping record they hold for connections with no traffic for a certain period. Without a keepalive, if that mapping times out, the server can no longer deliver packets to the client through that NAT, and the connection stays broken in one direction until the client sends traffic again. An empty keepalive packet sent every 25 seconds keeps that mapping alive and guarantees that the server can reach the client at any moment.

Verifying the connection

After the tunnel is enabled, the wg show command is used to see whether the connection has actually been established. This command lists, for each peer, when the mutual handshake took place, how many bytes have been received from and sent to that peer since, and the AllowedIPs value defined. If no recent handshake appears, the connection has most likely never been established; in that case the first things to check are whether the correct public keys have been entered on both sides, whether the UDP port is open on the server side, and whether the client can reach the address in the Endpoint field. WireGuard does not produce detailed log messages for connection errors the way OpenVPN does; that is why the wg show output and the handshake timestamp are the most practical reference for troubleshooting.

Shows the last handshake time and traffic statistics for all peers on the specified WireGuard interface.

wg show wg0

Common mistakes

  • Using the same key pair on more than one device. Every client must have its own private/public key pair; sharing keys both creates a security hole and can conflict with WireGuard's peer matching logic.
  • Setting AllowedIPs incorrectly. Defining a client's AllowedIPs value more broadly than necessary on the server side (so that it also covers another client's subnet, for example) leads to routing conflicts and traffic going to an unexpected peer.
  • Forgetting PersistentKeepalive on a client behind NAT. When this value is left blank the connection can become one-directional after a while; on mobile networks and behind home routers in particular this setting is all but mandatory.
  • Forgetting to open the UDP port in the firewall. WireGuard runs over 51820/UDP by default; unless that port is opened in the firewall on the server side (ufw, iptables, the cloud provider's security group and so on), clients can never connect to the server.
  • Leaving the permissions on the wg0.conf file loose. Because this file contains the private key in plain text, it should be closed off to everyone but root with chmod 600.

Once you have generated your keys, the remaining work is bringing these fields together with the right syntax. When writing by hand, a stray space, a missing line or wrong CIDR notation can leave the connection unable to establish at all.

This tool does not generate cryptographic keys; it only takes the real keys you produced with wg genkey and wg pubkey and turns them into valid Interface/Peer syntax. Once you have generated the keys and filled in this form, you can save the resulting file directly as /etc/wireguard/wg0.conf and enable it with wg-quick up wg0.