Why architecture matters here

The architectures WireGuard displaced were slow for structural reasons, not just implementation ones. IPsec splits into IKE (userspace key negotiation) and ESP (kernel data), with a large negotiable policy surface — algorithms, modes, lifetimes, identities — that has to be agreed before a byte flows and re-agreed on rekey; the flexibility is precisely what makes it hard to configure correctly and easy to misconfigure into weakness. OpenVPN runs the datapath in userspace over TLS, so every packet takes a trip across the kernel/user boundary and through a general-purpose TLS stack. Both carry connection state that stateful middleboxes track and reap, which is why long-idle VPNs drop and mobile VPNs thrash on network changes.

WireGuard's architecture matters because it removes those costs by removing the choices that caused them. With no negotiation, the handshake is a fixed 1-RTT exchange — a client can send its first data packet in the same round trip that establishes keys, so tunnel setup is imperceptible. With the datapath in the kernel and parallelized across CPUs, throughput approaches line rate and encryption is not a bottleneck on modern hardware. With no per-connection session state exposed to the network and silence when idle, a laptop can suspend for hours, wake on a different Wi-Fi network, and resume the same tunnel the instant it sends a packet — the server simply learns the new source endpoint from the authenticated packet and updates its record.

The trade-off is rigidity, and it is deliberate. You cannot pick a different cipher, you cannot do complex policy-based routing inside WireGuard itself, and you get no built-in key distribution — WireGuard moves those problems out of the data plane. Key management, address assignment, and access policy become the job of a control plane you build or adopt (a config manager, a mesh coordinator, an identity provider), and the tunnel stays a dumb, fast, auditable pipe. That separation is the source of both WireGuard's elegance and the most common operational surprises, because the parts it deliberately omits are parts you now own.

Advertisement

The architecture: every piece explained

Top row: identity and key exchange. Each peer holds a static Curve25519 keypair; its public key is its identity, and configuration lists remote peers by public key plus an optional last-known endpoint (IP:port). The heart of the config is cryptokey routing: each peer entry carries an AllowedIPs list. That list does double duty — inbound, a decrypted packet is only accepted if its source address falls within the sending peer's AllowedIPs (authentication as routing); outbound, the kernel selects the peer whose AllowedIPs contain the destination (routing as authentication). The Noise IK handshake runs in one round trip: initiator and responder mix their static and fresh ephemeral keys through a sequence of Diffie-Hellman operations, authenticating each other and deriving fresh session keys for ChaCha20-Poly1305 with forward secrecy from the ephemerals.

Middle row: the data plane and its state machine. Transport data packets are UDP with a tiny 4-byte type/reserved prefix, the receiver index, a 64-bit nonce counter, and the AEAD-sealed payload; a sliding anti-replay window rejects duplicated or too-old counters. A small set of timers governs everything: rekey after ~2 minutes of a key's use or 120 seconds of age, a handshake retry on failure, an optional PersistentKeepalive to hold a NAT mapping open, and a rekey-attempt ceiling before the tunnel is considered dead. Roaming is automatic: because peers are keys not addresses, an authenticated packet from a new source endpoint updates the peer's endpoint in place — the tunnel follows the client across networks with no renegotiation.

Bottom rows: performance and behavior. The kernel datapath processes packets without a userspace round trip and spreads encryption/decryption across CPUs via per-peer queues and multiqueue NICs, so a single tunnel scales with cores. Silence when idle is a property, not a setting: with no data and no keepalive configured, a tunnel emits zero packets, which is great for battery and stealth but means an idle NAT mapping can lapse — hence keepalive for peers behind NAT that must be reachable. The ops strip names the recurring work: rotating static keys, keeping AllowedIPs tight (they are your access control list), setting MTU to avoid fragmentation, and watching per-peer last-handshake and last-seen timestamps to know a tunnel is genuinely up.

WireGuard — a UDP tunnel that is a cryptokey-routing table, not a session protocolstatic keys, Noise handshake, kernel datapathPeer configstatic keypair + endpointsCryptokey routingpubkey -> allowed IPsNoise IK handshake1-RTT, ephemeral DHSession keysChaCha20-Poly1305Transport dataUDP + 4-byte typeNonce + counteranti-replay windowTimersrekey / keepaliveRoamingendpoint follows sourceKernel datapathper-CPU queues, no userspaceSilence when idleno traffic, no packetsOps — key rotation + allowed-IP hygiene + MTU + handshake/last-seen metricsencryptroutederivesealenqueueverifyrekeyoperateoperate
WireGuard: peers are identified by static public keys, cryptokey routing maps keys to allowed IPs, a 1-RTT Noise handshake derives ChaCha20-Poly1305 session keys, and the datapath lives in the kernel.
Advertisement

End-to-end flow

Follow a laptop connecting to a corporate gateway. The laptop's config lists one peer — the gateway — by public key, with Endpoint = gw.example.com:51820 and AllowedIPs = 10.0.0.0/8 (route the whole internal range through the tunnel). The gateway's config lists the laptop by its public key with AllowedIPs = 10.7.0.4/32 (this laptop owns exactly that one tunnel address). No shared secret beyond the key pairs, no negotiated policy.

The user opens an internal dashboard at 10.0.4.9. The kernel consults cryptokey routing, finds the destination inside the gateway peer's AllowedIPs, and needs a session. It fires the Noise IK handshake: an initiation message (the laptop's ephemeral public key plus encrypted static identity and a timestamp to defeat replay) travels to the gateway's UDP endpoint; the gateway responds with its ephemeral, both sides complete the DH mixing, and symmetric ChaCha20-Poly1305 keys exist one round trip later. The very first HTTPS packet is encapsulated, sealed, and sent — often in the same RTT — so the user perceives no VPN setup delay. Return traffic from 10.0.4.9 arrives at the gateway, which routes it to the laptop's tunnel address, seals it, and sends it to the laptop's current source endpoint.

Now the mobile reality. The user walks out of the building; Wi-Fi drops and LTE takes over, giving the laptop a new public IP. There is no reconnect. The next data packet the laptop sends arrives at the gateway from a new source address:port but is validly sealed under the existing session key and its inner source is still 10.7.0.4 — inside AllowedIPs. The gateway authenticates it, updates the peer's endpoint to the new address, and replies there. The tunnel roamed transparently. Two hours of suspend later, the story repeats: silence while idle means no packets crossed during suspend, and the first packet on wake either rides the still-valid session or triggers a fresh 1-RTT handshake if the rekey timer elapsed.

The failure texture is instructive too. If the gateway sits behind NAT and the laptop is the only initiator, everything is fine; but if a peer behind NAT must receive unsolicited traffic, its NAT mapping expires during idle silence and packets can't reach it until it speaks again — which is exactly what PersistentKeepalive = 25 solves by emitting a tiny packet every 25 seconds to keep the mapping warm. And if someone fat-fingers AllowedIPs to overlap two peers, cryptokey routing has no tiebreaker concept beyond most-specific match, so traffic silently goes to the wrong tunnel — an access-control bug wearing a routing costume.