WireGuard

Fast, modern, secure VPN tunnel

Remote Access & VPN ★ 4k stars Medium setup GPL-2.0

WireGuard is an extremely simple yet fast and modern VPN that uses state-of-the-art cryptography. It is built into the Linux kernel and is straightforward to self-host as a personal VPN.

Key features

  • Modern cryptography
  • Tiny codebase
  • High performance
  • Kernel-level integration

Pros & cons

Strengths

  • Very fast
  • Simple configuration

Trade-offs

  • No built-in user management

WireGuard replaces

Last reviewed Sep 13, 2026 · 881 words

WireGuard's most confusing property is that it never tells you anything went wrong. A peer with a mistyped key, a wrong AllowedIPs, or a blocked UDP port produces no error, no log line, no rejected connection: the tunnel simply carries no traffic, and wg show reports "latest handshake" as blank. This is deliberate (the server does not even respond to an unauthenticated datagram, so it is invisible to port scanners) and it is why a protocol with roughly 4,000 lines of kernel code and a config file of eight lines still gets called hard. Learn the four silent failures below and it stops being hard.

An entire server in eight lines

On any Linux with kernel 5.6 or later WireGuard is built in; apt install wireguard adds the wg and wg-quick tools. Generate keys and write /etc/wireguard/wg0.conf:

umask 077
wg genkey | tee server.key | wg pubkey > server.pub
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of server.key>
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = <the laptop's public key>
AllowedIPs = 10.8.0.2/32

Set net.ipv4.ip_forward=1 in sysctl, open UDP 51820 on the firewall and the router, then systemctl enable --now wg-quick@wg0. The laptop's config mirrors it with Endpoint = vpn.example.com:51820, AllowedIPs = 10.8.0.0/24 for split tunnelling or 0.0.0.0/0 to route everything, and PersistentKeepalive = 25 if the client sits behind NAT. That is the whole thing; it runs in 64 MB of RAM and adds around 1 ms of latency on a LAN.

The four ways it fails silently

Keys swapped. Each side's config holds its own private key and the other side's public key. Pasting the server's public key into the server's own [Peer] block, or a private key where a public one goes, gives no error and no handshake. Run wg show on both ends and compare the peer: lines against what you think you pasted.

AllowedIPs too narrow or overlapping. This field does two jobs: it is the firewall for what a peer may send, and it is the routing table for what gets sent to that peer. A client with AllowedIPs = 10.8.0.1/32 can reach the server and nothing behind it. Two peers with overlapping ranges make the second one steal the route from the first.

UDP not reaching the server. WireGuard has no TCP fallback. Corporate networks and hotel Wi-Fi that block UDP block WireGuard completely, and a router that forwards TCP 51820 by mistake looks identical from the client. Test with a phone on mobile data before blaming the config.

MTU. The default interface MTU of 1420 is right for most links; over PPPoE or some mobile carriers it is too large, and the symptom is that SSH works while any large transfer hangs. MTU = 1380 in the client [Interface] is the usual fix.

There is no user management, by design

WireGuard has no accounts, no groups, no revocation list, no key rotation and no way to push config to a device. A peer is a public key in a file. For one person and four devices that is fine and arguably ideal. For a household, a small team, or anyone who does not want to hand-edit a server file each time a phone is replaced, you add a layer. wg-easy is a single container that wraps this exact setup in a web page with QR codes for phones, and is the smallest possible step up. Tailscale, or the self-hosted control server Headscale, goes further: it keeps WireGuard as the data plane and adds identity, key distribution, NAT traversal so that neither side needs a public port, and access control. The trade is a coordination service in the loop. My remote access three ways piece compares raw WireGuard, a mesh, and a reverse proxy for the common cases.

Where raw WireGuard still wins

Site-to-site links between two routers, a static tunnel from a VPS to a home network so a home server can be exposed through a public IP, and the underlay for a Kubernetes cluster are all jobs where a mesh overlay's magic gets in the way and a fixed 8-line config that never changes is exactly right. Performance is the other case: kernel WireGuard on modest hardware pushes close to line rate on a gigabit link, where userspace overlays and OpenVPN do not.

What I'd do

For a single person: raw WireGuard on the server, configs generated once, wg show bookmarked, and the four failures above memorised. For anything with more than one human involved: keep the server, put wg-easy on it, and hand out QR codes. For a fleet of devices behind assorted NATs with no fixed public IP anywhere: Tailscale, or Headscale if the coordination server also has to be yours. In all three cases the tunnel underneath is the same few thousand lines of C, which is the reason to be comfortable with any of them.

Similar remote access & vpn apps