WireGuard VPN on Linux: Complete Server and Client Setup (2026)
WireGuard has fundamentally changed what a VPN can look like. Where OpenVPN ships roughly 600,000 lines of code, WireGuard accomplishes the same core task in around 4,000 lines — a surface area small enough for a single security researcher to audit over a weekend. It has been merged directly into the Linux kernel since version 5.6 (released March 2020), meaning there is nothing extra to compile or load on any modern distribution. On top of that, it uses exclusively state-of-the-art cryptography: the Noise protocol framework, ChaCha20-Poly1305 for symmetric encryption, Curve25519 for key exchange, and BLAKE2 for hashing.
The practical result is a VPN that is faster than OpenVPN in almost every benchmark, simpler to configure than IPsec, and easier to audit than both. This tutorial walks through a complete setup: server installation, key generation, client configuration, mobile provisioning via QR code, and post-quantum pre-shared keys.
Why WireGuard Instead of OpenVPN
OpenVPN is mature and battle-tested, but its complexity is a liability. Its TLS-based handshake is slower than WireGuard's one-round-trip Noise handshake. Its configuration files can run to hundreds of lines. Its certificate infrastructure requires a PKI. WireGuard replaces all of that with a single config file per peer and a pair of Curve25519 keys.
Because WireGuard lives in the kernel, packets are processed without crossing the user/kernel boundary on every send and receive. Throughput benchmarks routinely show WireGuard achieving two to three times the throughput of OpenVPN on the same hardware, with noticeably lower CPU usage and latency.
WireGuard also has a deliberately minimal attack surface. There is no negotiation of cipher suites — the algorithms are fixed and modern. There is no server that responds to unauthenticated packets; the interface is cryptographically silent to anyone who does not hold a valid key.
Prerequisites
- A Linux server with kernel 5.6 or later. Ubuntu 22.04 LTS, Debian 12, Fedora 40, and any current RHEL-compatible release all qualify.
- A public IPv4 (or IPv6) address on the server.
- UDP port 51820 open in your firewall and any cloud security group rules.
- Root or sudo access on both the server and client machines.
You can verify your kernel version with:
uname -r
Any output of 5.6 or higher means WireGuard is already present in the kernel. The wireguard-tools package (which provides wg and wg-quick) is the only userspace component you need to install.
Step 1: Install WireGuard Tools
On Ubuntu or Debian:
sudo apt update && sudo apt install wireguard
On RHEL, AlmaLinux, or Rocky Linux:
sudo yum install wireguard-tools
On Fedora:
sudo dnf install wireguard-tools
The wireguard package on Debian/Ubuntu also pulls in the kernel module for older kernels where it is not built in. On kernel 5.6+ it simply installs the userspace utilities.
Step 2: Generate Server Keys
WireGuard uses Curve25519 key pairs. The private key never leaves the machine it was generated on; the public key is shared with peers. Generate the server keys now:
cd /etc/wireguard && umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key
umask 077 ensures that both files are created with permissions 600 — readable only by root. You can confirm this with ls -la /etc/wireguard/. Never share or commit the private key file.
Step 3: Enable IP Forwarding
For the server to route traffic between peers and to the internet, the kernel must forward packets. Enable this permanently by editing /etc/sysctl.conf:
sudo nano /etc/sysctl.conf
Add or uncomment the following line:
net.ipv4.ip_forward=1
Apply the change immediately without rebooting:
sudo sysctl -p
If you also need IPv6 forwarding, add net.ipv6.conf.all.forwarding=1 as well.
Step 4: Create the Server Configuration
Create /etc/wireguard/wg0.conf. Replace <server_private_key> with the contents of /etc/wireguard/server_private.key and <client_public_key> with the client's public key (generated in the next step):
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <server_private_key>
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.0.0.2/32
A few notes on each field:
Addressassigns the server's IP address within the VPN subnet.ListenPortis the UDP port WireGuard listens on. 51820 is the conventional default.PostUpandPostDownrun iptables rules when the interface comes up and goes down. TheMASQUERADErule performs NAT so that client traffic appears to originate from the server's public IP. Replaceeth0with your actual outbound interface name (check withip route | grep default).AllowedIPsin a[Peer]block acts as both a routing rule and an access control: only packets sourced from10.0.0.2will be accepted from this peer.
Step 5: Generate Client Keys and Configuration
On the client machine (or on the server, keeping the client private key secure):
cd /etc/wireguard && umask 077
wg genkey | tee client_private.key | wg pubkey > client_public.key
Now create the client configuration file, client.conf:
[Interface]
Address = 10.0.0.2/32
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
AllowedIPs = 0.0.0.0/0routes all traffic through the VPN (full-tunnel mode). To route only VPN subnet traffic, use10.0.0.0/24instead.PersistentKeepalive = 25sends a keepalive packet every 25 seconds. This is important when the client is behind NAT, as it keeps the NAT mapping alive so the server can send packets back.DNSsets the DNS resolver used while the tunnel is active.
Step 6: Start the WireGuard Interface
Bring the server interface up:
sudo wg-quick up wg0
Enable it to start automatically on boot:
sudo systemctl enable wg-quick@wg0
Do the same on the client:
sudo wg-quick up /path/to/client.conf
Or, if you placed the config at /etc/wireguard/wg0.conf on the client:
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
Step 7: Adding Multiple Peers
Each additional client gets its own [Peer] block on the server, with a unique IP in AllowedIPs. For example, a second client using 10.0.0.3:
[Peer]
PublicKey = <client2_public_key>
AllowedIPs = 10.0.0.3/32
You can add new peers to a running interface without restarting it:
sudo wg set wg0 peer <client2_public_key> allowed-ips 10.0.0.3/32
To make the change persistent, add the [Peer] block to /etc/wireguard/wg0.conf as well.
Peer assignments should be tracked carefully. Each AllowedIPs value must be unique across all peers — overlapping ranges will cause routing ambiguity.
Step 8: Mobile Clients via QR Code
The WireGuard apps for iOS and Android can import a configuration by scanning a QR code. Generate one from the client config file using qrencode:
sudo apt install qrencode
qrencode -t ansiutf8 < client.conf
The QR code is printed directly in the terminal using Unicode block characters. Open the WireGuard app on your phone, tap the "+" button, choose "Scan from QR Code", and point the camera at your terminal. The configuration is imported instantly without copying any keys manually.
Keep in mind that displaying a private key as a QR code on screen is a security consideration — do this in a private environment.
Step 9: Pre-Shared Keys for Post-Quantum Resistance
WireGuard's Curve25519 key exchange is secure against classical computers. However, a sufficiently powerful quantum computer could break elliptic-curve Diffie-Hellman in the future. WireGuard supports an optional pre-shared key (PSK) that is mixed into the key derivation, adding a symmetric layer of protection that quantum computers cannot break.
Generate a PSK:
wg genpsk > peer_psk.key
Add it to both the server's [Peer] block and the client's [Peer] block:
[Peer]
PublicKey = <peer_public_key>
PresharedKey = <contents_of_peer_psk.key>
AllowedIPs = 10.0.0.2/32
The PSK must be the same on both sides and should be treated with the same care as a private key.
Step 10: Verify the Connection
On the server, check the current state of all peers:
sudo wg show
The output lists each peer's public key, its last handshake time, and the bytes transferred. A handshake time within the last few minutes means the peer is connected. From the client, verify basic connectivity:
ping 10.0.0.1
A successful ping to the server's VPN IP confirms the tunnel is working. For full-tunnel clients, also verify that outbound traffic is routed correctly:
curl ifconfig.me
This should return the server's public IP rather than the client's.
Troubleshooting
No handshake / connection times out
- Confirm UDP port 51820 is open:
sudo ufw allow 51820/udp(if using UFW), or check your cloud firewall rules. - Capture traffic on the server to see if packets are arriving:
sudo tcpdump -i eth0 port 51820 - Verify that the public keys in each side's config match the actual keys generated.
Traffic reaches server but cannot reach the internet
- Check that IP forwarding is enabled:
cat /proc/sys/net/ipv4/ip_forwardshould return1. - Verify the iptables
MASQUERADErule is active:sudo iptables -t nat -L -n -v. - Confirm the interface name in
PostUp/PostDownmatches your actual outbound interface.
Interface fails to come up
- Check for syntax errors in the config file:
sudo wg-quick up wg0will print the error. - Ensure the private key in the config has no extra whitespace or newlines.
Security Hardening
Protect all key files with strict permissions:
sudo chmod 600 /etc/wireguard/*.key
sudo chmod 600 /etc/wireguard/wg0.conf
Remove peers that are no longer in use promptly. An inactive peer with a valid public key is still an authorized endpoint — removing it from the config (and running sudo wg set wg0 peer <pubkey> remove) eliminates the access immediately. Consider rotating keys periodically for long-lived deployments. If you are using pre-shared keys, store them in a secrets manager rather than plain text files where possible.
Conclusion
WireGuard gives you a production-grade VPN with a configuration that fits on a single screen. The combination of kernel-level performance, a minimal codebase, and modern fixed-algorithm cryptography makes it the right default choice for new VPN deployments on Linux in 2026. Once the server is up, adding a new client takes less than a minute: generate a key pair, add a [Peer] block, and either transfer the client config file or display a QR code.