WireGuard replaced OpenVPN as the default modern VPN for most operators because it is dramatically simpler, demonstrably faster, and built into the Linux kernel. This guide walks through installing it, generating keys, connecting peers, setting up a full-tunnel killswitch, and rotating keys. It also compares raw WireGuard to Tailscale and Headscale, because the right answer to 'do I need WireGuard' is often 'no, you need a managed mesh on top of WireGuard'.
What WireGuard is, and what it replaces
WireGuard is a VPN protocol and a Linux kernel module. It connects two or more machines into an encrypted virtual network, where traffic that goes between them looks like traffic on a private LAN. The protocol is small, the codebase is small, the configuration is small. That smallness is the headline feature.
The thing it replaces, for most operators, is OpenVPN. OpenVPN is mature and reliable, but the configuration sprawls across certificates, certificate authorities, client config files, server config files, TLS profile choices, cipher suites, and an option list that runs to four pages. WireGuard does the same job with a single configuration file per peer and no PKI.
It also replaces IPsec for the use case "I want a tunnel between two machines without setting up two days of infrastructure." IPsec is still the right answer for site-to-site links between routers from different vendors that need to interop without coordination, but for two Linux boxes that you control, WireGuard is dramatically less work.
What WireGuard is not: a NAT traversal solution, a key management system, or a coordination layer for hundreds of peers. It is the data plane only. The minute you need any of those things on top, you reach for Tailscale, Headscale, or a homegrown distribution system. We come to those in the last section.
Topology choices
Before installing anything, decide what shape your VPN is. Three common ones:
- Point-to-point. Two machines talk to each other directly. Both need to be reachable from one direction, usually meaning at least one has a public IP. This is the easiest config and the right shape for "VPS A wants to reach VPS B over an encrypted link."
- Hub-and-spoke. One machine (the hub) has a public IP and accepts inbound connections from many spokes. Spokes can be laptops behind NAT, home servers, IoT devices. Spokes route traffic through the hub, including to each other if you allow it.
- Mesh. Every peer talks to every other peer directly. Faster than hub-and-spoke (no hub hop) but harder to bootstrap (every peer needs every other peer's public key) and breaks if peers are behind NAT.

For most operators the right answer is hub-and-spoke with a VPS as the hub. The hub is cheap, the public IP solves NAT for the spokes, and the configuration of new spokes only touches two files (the new spoke and the hub).
Installing WireGuard on Linux
On any kernel newer than 5.6 (everything shipped after 2020), WireGuard is in the kernel already. You only install the userspace tools.
# Debian and Ubuntu
sudo apt-get update
sudo apt-get install -y wireguard wireguard-tools
# Fedora and RHEL family
sudo dnf install -y wireguard-tools
# Arch
sudo pacman -S wireguard-tools
# Alpine
sudo apk add wireguard-tools
Verify the kernel module is there:
sudo modprobe wireguard
ip link add dev wg0 type wireguard
ip link delete wg0
If those commands succeed without error, WireGuard is ready. If the modprobe fails, you are on an old kernel and need to install the DKMS package or upgrade the kernel. The latter is the right answer in 2026.
Generating keys
Each peer has one keypair. The private key stays on that machine. The public key is shared with peers that should be allowed to talk to this one.
umask 077
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
The umask 077 matters. The private key file should not be world-readable. Check the permissions afterwards.
ls -l /etc/wireguard/private.key
# -rw------- 1 root root 45 Jun 10 18:00 /etc/wireguard/private.key
You can also generate a preshared key per peer pair, which adds a layer of post-quantum padding to the handshake. It is not strictly necessary for most threat models but it is free to add.
wg genpsk > /etc/wireguard/preshared.key
chmod 600 /etc/wireguard/preshared.key
A first peer-to-peer link
Two machines: a hub at 203.0.113.10 with private VPN address 10.0.0.1, and a spoke with private VPN address 10.0.0.2.
On the hub, /etc/wireguard/wg0.conf:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = HUB_PRIVATE_KEY_HERE
[Peer]
PublicKey = SPOKE_PUBLIC_KEY_HERE
AllowedIPs = 10.0.0.2/32
On the spoke, /etc/wireguard/wg0.conf:
[Interface]
Address = 10.0.0.2/24
PrivateKey = SPOKE_PRIVATE_KEY_HERE
[Peer]
PublicKey = HUB_PUBLIC_KEY_HERE
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25
The PersistentKeepalive = 25 on the spoke tells it to send a tiny packet every 25 seconds, which keeps the NAT translation alive when the spoke is behind a typical home router. Without it, the tunnel times out from the hub's perspective whenever it sits idle.
Bring the tunnels up:
# On both peers
sudo systemctl enable --now wg-quick@wg0
sudo wg show
wg show on the hub will display the spoke as a peer with a recent handshake timestamp once they have talked. Ping 10.0.0.1 from the spoke; the reply should come from the hub via the tunnel.
Open the UDP port on the hub's firewall:
sudo ufw allow 51820/udp comment "WireGuard"
Full tunnel vs split tunnel
The AllowedIPs setting on the spoke side controls which traffic flows through the VPN.
Split tunnel, the default in the example above, means only traffic to the listed IPs goes through the VPN. The spoke's other traffic (browsing, system updates, everything else) goes out the spoke's normal internet connection. The VPN is purely a tunnel to a specific set of resources.
Full tunnel means all traffic goes through the VPN. To make this happen, change the spoke's AllowedIPs to 0.0.0.0/0, ::/0:
# On the spoke, in the Peer block
AllowedIPs = 0.0.0.0/0, ::/0
For the hub to actually route that traffic out to the internet, it needs IP forwarding and a masquerade rule:
# On the hub
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
echo 'net.ipv6.conf.all.forwarding=1' | sudo tee -a /etc/sysctl.d/99-wireguard.conf
# nftables masquerade for traffic leaving the public interface
sudo tee /etc/nftables.conf <<'EOF'
table inet nat {
chain postrouting {
type nat hook postrouting priority 100;
oifname "eth0" masquerade
}
}
EOF
sudo systemctl enable --now nftables
Replace eth0 with the actual public-facing interface on the hub. The masquerade rule rewrites the source IP of forwarded traffic so the public internet sees it as coming from the hub.
A killswitch with nftables
The point of a killswitch is that if the WireGuard tunnel drops, the client refuses to send any traffic outside the tunnel. Without it, a dropped tunnel silently fails open and your "private" traffic goes out the normal interface.

On the spoke, the cleanest way is with PostUp and PostDown hooks in the WireGuard config that install and remove an nftables rule that drops non-VPN traffic. Add to the [Interface] section:
PostUp = nft add table inet wg-killswitch; \
nft add chain inet wg-killswitch output '{ type filter hook output priority 0; policy accept; }'; \
nft add rule inet wg-killswitch output oifname != "wg0" ip daddr != 203.0.113.10 fib daddr type != local drop
PostDown = nft delete table inet wg-killswitch
That rule allows traffic only out the WireGuard interface, except for the bootstrap connection to the hub itself, and local-loopback. If wg0 goes down, the rule blocks everything; the spoke loses connectivity until wg0 comes back up. Refusing to leak is the point.
Rotating keys
WireGuard does not rotate keys for you. There is no certificate expiry, no built-in rotation API. You rotate manually or via the same configuration management system that distributes the configs in the first place.
The rotation pattern:
- Generate a new keypair on the peer being rotated.
- Update the peer's local
wg0.confwith the new private key. - On every peer that listed the old public key, update to the new public key.
- Restart WireGuard on each affected peer.
The lack of a revocation mechanism is by design. Removing a peer's public key from your config is the revocation. There is no separate CRL to maintain.
For a fleet with more than four or five peers, hand-rotating gets tedious fast. This is the point at which you reach for Tailscale or Headscale.
WireGuard, Tailscale and Headscale
WireGuard is a data plane. Once you have more than a handful of peers, you need a control plane on top: something that distributes public keys, handles NAT traversal, coordinates ACLs, and lets you add or remove peers without touching every other peer's config.
Tailscale is the commercial control plane. You install the Tailscale client on each machine, log in with your identity provider, and the network forms. NAT traversal works in cases where raw WireGuard would not (Tailscale's DERP servers act as fallback relays). ACLs are policy as code. The data plane underneath is still WireGuard, you just stop configuring it directly.
Headscale is the open-source Tailscale-compatible control server. Same client, you point it at your own coordinator. The cost is that you operate the coordinator. The advantage is that you do not depend on a SaaS.
For solo operators with three or four peers in a hub-and-spoke shape, raw WireGuard is fine. For anything bigger, the coordination layer is the part you actually need; the choice is mostly Tailscale (managed) versus Headscale (self-hosted).
Picking the right pattern
Four common scenarios and the right answer for each:
- Two VPSes need an encrypted private link. Raw WireGuard, peer-to-peer. One INI file each. This is the canonical case where WireGuard is undefeatable.
- You want to route your laptop's traffic through a VPS so coffee-shop Wi-Fi cannot see it. Raw WireGuard, hub-and-spoke with the VPS as hub, full-tunnel config on the spoke, killswitch on the spoke. Twenty lines of config.
- You have a dozen devices (laptops, home servers, IoT, work machines) and want them to reach each other. Tailscale or Headscale. The coordination cost of raw WireGuard at this scale exceeds the benefit of running the data plane yourself.
- Site-to-site between two offices, both behind their own NAT. Tailscale or Headscale unless one side can get a public IP. Raw WireGuard cannot punch through symmetric NAT; Tailscale's DERP fallback can.

WireGuard, like SSH, is a tool that rewards reading the small fine print once and then using it routinely. There is not much to it. The protocol design assumes you will treat key compromise as "remove the key from every peer that lists it"; the configuration model assumes you will keep things simple. Both assumptions are correct. The tool is doing less than its predecessors because the previous generation of VPNs was doing too much.
Why pick WireGuard over OpenVPN in 2026?
WireGuard is faster, simpler and built into the kernel. OpenVPN still has its place when you need TCP fallback through a hostile firewall, but for any normal use case WireGuard wins on every axis. The codebase is roughly a tenth the size of OpenVPN, which matters for the auditable-security argument too.
Is WireGuard "set and forget" once it is running?
For a single peer-to-peer tunnel, yes. The kernel does not lose tunnels. When something does break, it is usually NAT traversal on a road-warrior client, the keepalive setting being wrong, or a firewall rule. The protocol itself is reliable to the point of being boring.
Does WireGuard work through a corporate firewall that only allows TCP 443?
No. WireGuard is UDP-only by design. Wrappers exist (wstunnel, udp2raw) that put a TCP layer over WireGuard, but they reintroduce a lot of what WireGuard removed. For corporate-firewall traversal, an OpenVPN-over-TCP fallback or Cloudflare Tunnel is more honest.
Can WireGuard connect a laptop to a home server behind NAT?
Not directly. The hub needs a public IP. The usual pattern is a small VPS as the hub, with both the laptop and the home server as spokes, both connecting outbound. Tailscale solves this with NAT traversal so neither side needs a public IP; raw WireGuard does not.
What is the performance like?
On a 1 Gbps link with modern CPUs, WireGuard runs at line rate with single-digit per-cent CPU. The kernel implementation is the reason. Userspace implementations (wireguard-go) are slower but still good. The bottleneck for most operators is not WireGuard.
How is key compromise handled?
Generate new keys for the affected peer, update its config and the configs of every peer that lists it. There is no revocation list; the act of removing the old public key from peers' AllowedIPs is the revocation. This is one of the operational simplifications WireGuard makes.
Should I use Tailscale?
If you have more than four or five peers, or peers that come and go, almost certainly yes. Tailscale handles the coordination, NAT traversal, key distribution and ACL layer that you would otherwise build yourself. Self-hosters who do not want to depend on a SaaS use Headscale, which is an open-source compatible coordinator.
Content Delivery Networks: A Complete Guide
How CDNs make the web fast, how they actually work, and how to pick one
Domain Names: A Complete Guide
How domains work, what gTLD vs ccTLD really means, and how to pick and protect one
Object storage explained: S3, R2, B2 and self-hosted MinIO
A reference for picking an object storage provider, understanding the egress trap, and knowing the parts of the S3 API that actually matter.
Cloud Hosting: A Complete Guide
The service models, deployment patterns, and pricing traps every buyer should understand