Setting Up WireGuard VPN on Linux: Lean, Modern Encryption
AI generated
$
/etc
Linux
WireGuard VPN
Setting up lean, modern encryption on Linux

OpenVPN and IPsec have been the standard for site to site VPNs for years, but they carry a code footprint, configuration complexity, and handshake overhead that WireGuard deliberately avoids. With roughly four thousand lines of kernel code, modern cryptography, and a single configuration file, WireGuard has established itself as a lean alternative for secure server to server connections.

10 min read Linux VPN Networking

1. Why WireGuard is lighter than OpenVPN and IPsec

OpenVPN is built on OpenSSL and offers an enormous range of configurable ciphers, authentication methods, and operating modes, which in practice leads to configuration files with dozens of options and a correspondingly large attack surface. IPsec, in turn, spreads its logic across several protocols like IKE, ESP, and AH, whose interplay regularly poses debugging challenges even for experienced administrators, especially once NAT traversal comes into play.

WireGuard takes a radically different approach: it offers exactly one modern cryptography suite, Curve25519 for key exchange and ChaCha20-Poly1305 for authenticated encryption, with no negotiation between multiple ciphers. This deliberate constraint keeps the kernel code down to roughly four thousand lines, compared to sometimes several hundred thousand lines in established IPsec implementations, which drastically shrinks both the attack surface and the number of potential implementation bugs.

2. The Noise Protocol Framework and the lean handshake

WireGuard is built on the Noise Protocol Framework, a formally verified collection of handshake patterns for authenticated key exchange. The handshake it uses, known as Noise_IK, needs only a single round trip between two peers to establish a new session with perfect forward secrecy, while classic IPsec IKEv2 handshakes go through several round trips depending on configuration.

Every connection is also conceptually stateless compared to the classic connection model: WireGuard has no explicit connection in the sense of a TCP handshake, instead processing UDP packets based on cryptographic identity. That means a client can change its public IP address, for example when switching between mobile data and WiFi, and the connection survives once the next authenticated packet arrives, with no need for a full renegotiation.

3. Generating key pairs: the first step of any WireGuard configuration

WireGuard uses a dedicated Curve25519 key pair for every peer, consisting of a private and a public key. Unlike classic VPN solutions with a central certificate authority, there is no public key infrastructure, no certificate chains, and no revocation lists to manage, which significantly simplifies initial setup, but also means key rotation and peer management across many servers must be handled organizationally.

The private key ideally never leaves the server it was generated on, while the public key is handed to the peer and entered into its peer configuration. For environments with many servers, a central but securely stored directory of public keys is worthwhile, for example in a secrets management system, while private keys stay strictly local with restrictive file permissions.


# Generate a private key and restrict permissions immediately
umask 077
wg genkey > /etc/wireguard/privatekey

# Derive the public key from the private key
wg pubkey < /etc/wireguard/privatekey > /etc/wireguard/publickey

cat /etc/wireguard/publickey

4. Interface configuration: the structure of wg0.conf in detail

The central configuration file of a WireGuard interface follows a simple INI style structure with exactly two section types: a single [Interface] block for the local configuration and any number of [Peer] blocks, one for each remote party. The [Interface] section sets the private key, the IP address assigned to the interface within the VPN subnet, and the UDP listen port through which WireGuard accepts incoming handshakes.

Optionally, the [Interface] block can carry extra shell logic via PostUp and PostDown, for example to automatically set firewall forwarding rules when the interface comes up and remove them again when it goes down. These hooks are especially useful in server to server scenarios where the WireGuard interface acts as a gateway for further network traffic, such as database replication that should be routed through the tunnel.


# /etc/wireguard/wg0.conf on the primary database server
[Interface]
PrivateKey = <private_key_server_a>
Address = 10.10.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT

[Peer]
# Replication standby at the second site
PublicKey = <public_key_server_b>
AllowedIPs = 10.10.0.2/32
Endpoint = replica.example.com:51820
PersistentKeepalive = 25

5. Peer setup and the meaning of AllowedIPs

AllowedIPs serves a dual purpose in WireGuard that is often underestimated: it defines which source IP addresses are accepted from a given peer, and it also acts as the routing table for outbound traffic that should be tunneled through that peer. Setting AllowedIPs too broadly, for example to 0.0.0.0/0, unintentionally routes the server's entire traffic through the tunnel, while defining it too narrowly silently drops legitimate packets without producing an error message.

For a server to server scenario with database replication across two sites, it is usually enough to restrict AllowedIPs to the single /32 address of each peer within the VPN subnet. If entire subnets behind a peer also need to be reachable, for example an internal network segment at the second site, the corresponding subnet is added to AllowedIPs and must additionally be supported by IP forwarding and matching routing entries on both sides.

6. PersistentKeepalive: keeping connections stable behind NAT

Since WireGuard is UDP based and has no explicit connection state like TCP, NAT gateways and stateful firewalls can drop the mapping between a client port and a session after a period of inactivity. Without a countermeasure, that means incoming packets from the server can no longer be delivered to the original client once its NAT mapping expires, effectively breaking the connection in one direction.

The PersistentKeepalive option solves this by sending an empty UDP packet to the peer at fixed intervals, typically every 25 seconds, to artificially keep the NAT mapping alive. For pure server to server connections with fixed, publicly reachable IP addresses on both sides, PersistentKeepalive is often unnecessary, but it is recommended once at least one side sits behind NAT or a restrictive firewall with a session timeout.


# Bring the interface up and check status including handshake age
wg-quick up wg0
wg show wg0

# Expected output shows the latest handshake and transferred bytes
# peer: <public_key>
#   endpoint: replica.example.com:51820
#   allowed ips: 10.10.0.2/32
#   latest handshake: 12 seconds ago
#   transfer: 4.21 MiB received, 3.98 MiB sent

7. MTU considerations and best practices for production use

WireGuard wraps every packet in UDP and adds its own protocol header, which is why the interface defaults to an MTU of 1420 bytes instead of the usual 1500 bytes, to avoid fragmentation inside the tunnel. If WireGuard runs on top of an already reduced base MTU, for example inside another tunnel or over a PPPoE connection, the WireGuard MTU needs to be adjusted manually, since otherwise the same Path MTU Discovery problems that affect other tunneling technologies can show up here too.

For production use, it is also worth never storing private keys with world readable permissions, consistently using mode 600 with root ownership instead, and keeping AllowedIPs as narrow as possible rather than defaulting to 0.0.0.0/0 out of convenience. On Debian and Ubuntu systems, WireGuard can also be configured natively through systemd-networkd, which, compared to the classic wg-quick script, has the advantage that interface state and routing are managed consistently through the same network management as every other interface on the server.


# Manually adjust a WireGuard interface's MTU if the underlying
# path already has a reduced MTU of its own
ip link set mtu 1380 dev wg0

# Check the private key's permissions before going to production
chmod 600 /etc/wireguard/privatekey
chown root:root /etc/wireguard/privatekey

8. Practical example: securing database replication across sites

A typical use case in a hosting context is securing MySQL or PostgreSQL replication between two physically separated data centers without sending replication traffic unprotected over the open internet. Instead of securing the database itself with per connection TLS certificates, WireGuard wraps the entire traffic at the network layer, so the database configuration stays unchanged and simply points at the peer's internal VPN address.

In practice, the primary database server binds only to its WireGuard address in the 10.10.0.0/24 subnet, while the public network interface stays closed for the database port. That way, the replication link stays protected even if an attacker could otherwise reach a server's public database port directly, since the actual replication traffic runs exclusively inside the encrypted WireGuard tunnel.

9. Troubleshooting and monitoring WireGuard connections

The most important diagnostic signal is the latest handshake timestamp in the output of wg show: if that value is far in the past or missing entirely, no valid communication is happening between the peers, usually due to a wrong public key, UDP packets blocked at the firewall level, or a misconfigured endpoint. Since WireGuard deliberately stays silent on invalid packets instead of sending error messages, checking transferred byte counts often helps more than classic logging.

For ongoing monitoring, wg show wg0 dump produces machine readable output that can be fed into monitoring systems such as Prometheus through a simple exporter, tracking handshake age and transfer volume per peer continuously. A handshake older than about three minutes reliably indicates a problem in production server to server connections and should trigger an alert before replication itself starts to stall.

Aspect OpenVPN IPsec WireGuard
Code footprint Tens of thousands of lines, OpenSSL dependency Several hundred thousand lines across IKE/ESP/AH Roughly four thousand lines in the kernel
Configuration effort Certificates, many options IKE policies, often complex NAT traversal One file per interface, key pairs
Handshake TLS handshake with several round trips IKEv2 with several round trips Noise_IK with a single round trip
Cryptography choice Negotiable across many ciphers Negotiable across many suites Fixed, no negotiation overhead
Typical use Client VPN with certificate management Site linking in enterprise environments Server to server and lean site to site VPNs

Mironsoft

Server administration, Docker hosts, and performance tuning

Linux servers nobody on the team really understands anymore?

We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.

Server Audit

Review the existing server configuration for security gaps and performance bottlenecks.

Docker Host Setup

Set up and secure production-ready Docker environments for Magento cleanly.

Monitoring & Tuning

Measure resource usage and tune systemd, kernel, and services with purpose.

10. Summary

WireGuard VPN

Cryptography

Curve25519 for key exchange, ChaCha20-Poly1305 for encryption

Core configuration

One wg0.conf per interface with interface and peer blocks

Critical option

AllowedIPs controls both access and routing at once

Typical use case

Encrypted server to server connections like database replication

11. FAQ: WireGuard VPN

1Is WireGuard more secure than OpenVPN or IPsec?
WireGuard uses modern, fixed cryptography with no negotiation options, which reduces the attack surface. Security still depends heavily on correct key management, so none of the three solutions is categorically more secure.
2Can I run multiple peers on one WireGuard interface?
Yes, an interface can contain any number of peer blocks. Each peer is uniquely identified by its public key and associated AllowedIPs.
3What happens if two peers use the same AllowedIPs range?
WireGuard does not allow overlapping AllowedIPs between different peers on the same interface, since incoming traffic could no longer be assigned unambiguously. Configuration needs to be planned accordingly beforehand.
4Do I need PersistentKeepalive between two servers with fixed public IPs?
Usually not, since without NAT there are no mapping timeouts to worry about. With restrictive firewalls that have short UDP session timeouts, it can still be worth enabling as a safeguard.
5How do I rotate keys without breaking the connection?
A new key pair is generated, the new public key is added to all peers beforehand, and only then is the new private key activated locally, so both keys briefly work in parallel.
6Can WireGuard be used for IP forwarding between two network segments?
Yes, the relevant subnets need to be added to AllowedIPs, IP forwarding enabled in the kernel, and matching routing and firewall rules set via PostUp.
7Why doesn't WireGuard respond to invalid connection attempts?
WireGuard is deliberately designed to stay silent on packets without valid cryptographic authentication, so it never reveals information about active peers or the cause of a failure to an attacker.
8How do I monitor WireGuard connections continuously?
The command wg show wg0 dump provides machine readable data on handshake timing and transfer volume, which can be fed into monitoring systems like Prometheus via an exporter.
9Is WireGuard performant enough for database replication over the internet?
Yes, thanks to its low encryption overhead and kernel integration, WireGuard typically achieves close to native network throughput, making it well suited to replication heavy server to server links.
10Does the WireGuard port have to be publicly reachable?
At least one side of the connection needs a reachable endpoint with an open UDP port so the initial handshake can happen. Both sides behind restrictive NAT without an endpoint won't work reliably without additional tooling.