WireGuard vs Tailscale vs NetBird vs Headscale: Edge Fleet Networking (2026)

WireGuard vs Tailscale vs NetBird vs Headscale: Edge Fleet Networking (2026)

WireGuard vs Tailscale vs NetBird vs Headscale: Edge Fleet Networking (2026)

You have 400 gateways in 400 cabinets. Half sit behind a cellular modem on carrier-grade NAT, a quarter are on plant networks where the IT team will open exactly nothing, and the rest are on customer premises you will never visit. You need to reach each one for SSH, for a Modbus poll, for a container pull, and you need the answer to “who can reach what” to be auditable. That is the problem behind the WireGuard vs Tailscale question, and it is not really a question about cryptography at all.

WireGuard is a superb data plane with no opinion about how peers find each other or who may talk to whom. Tailscale, NetBird and Headscale are three different answers to the missing half: a control plane that distributes keys, endpoints and policy, plus the relay machinery for the networks where direct paths are impossible. In an edge fleet, that control plane is the product, and its failure modes are your operational risk.

You leave with a mental model of what each layer does, how NAT traversal succeeds or fails on cellular links, where each tool’s published limits sit, and a decision matrix with runnable configuration.

What this covers: WireGuard’s protocol and why it cannot scale operationally on its own; the control plane and relay design of Tailscale, Headscale and NetBird; NAT traversal mechanics with real numbers; ACLs, tags and subnet routers for OT; throughput reasoning; failure modes; and a decision framework for edge fleets.

Context and Background

WireGuard is a Layer 3 VPN built on a small, fixed cryptographic suite: Curve25519 for key agreement inside a Noise_IKpsk2 handshake, ChaCha20-Poly1305 for authenticated encryption, BLAKE2s for hashing, and HKDF for key derivation, all carried over UDP (source: the WireGuard protocol page at wireguard.com/protocol). It lives in the Linux kernel, has userspace implementations such as wireguard-go for other platforms, and its configuration is a short file of keys and allowed IP ranges. The small attack surface is the reason the industry adopted it as the substrate for nearly every modern mesh VPN.

What WireGuard deliberately omits is everything around the tunnel. There is no peer discovery, no user identity, no key rotation workflow beyond the session rekey, no policy language, no relay, and no NAT traversal beyond the fact that the listen port and the send source port are the same. In a two-site setup you paste two public keys and one endpoint and you are done. In an N-node mesh you maintain N(N-1)/2 relationships, and every join or key rotation is a configuration push to every other peer.

That arithmetic is the whole reason control planes exist. For 400 nodes in a full mesh there are 79,800 possible pairs; for 4,000 nodes it is nearly eight million. Nobody hand-edits that, and most fleets do not need a full mesh anyway, which is why policy is the real design axis.

The 2026 landscape has four practical shapes. Plain WireGuard plus your own automation (Ansible, Terraform, a hub-and-spoke topology) is the minimalist option. Tailscale is the commercial reference, with a hosted coordination server and a global relay network. Headscale is an independent open-source reimplementation of the Tailscale control server, using the Tailscale clients. NetBird is a separate open-source stack with its own clients, management, signal and relay services. Netmaker and ZeroTier exist in the same neighborhood, and I touch them briefly later.

If you run workload identity or fleet orchestration alongside this, the overlay interacts with both: see our notes on SPIFFE and SPIRE workload identity for identity above the network layer, and on k3s at the edge for what typically runs on the other side of these tunnels. For the underlying protocol detail, the WireGuard protocol description is short and worth reading in full.

One framing note before the architecture. Everything below that concerns vendor behavior was checked against vendor documentation during the writing of this post (October 2026). Where I could not verify a number, I say so rather than quote from memory, because licensing terms, plan limits and relay implementations in this space change several times a year.

Reference Architecture: Data Plane, Control Plane, Relay Plane

A mesh VPN splits into three planes: a data plane (WireGuard tunnels carrying encrypted packets between peers), a control plane (a service that registers nodes and distributes public keys, endpoint candidates and access policy), and a relay plane (servers that forward encrypted packets when no direct path exists). Plain WireGuard supplies only the first; Tailscale, Headscale and NetBird each supply all three.

WireGuard vs Tailscale architecture: data plane, control plane and relay plane for an edge mesh VPN

Figure 1: The three planes of an edge mesh VPN. The control plane never sees payloads; the relay forwards ciphertext only when direct UDP fails.

Figure 1 shows the structural point that matters for operations: the control plane and the data plane fail independently. If the coordination service goes down, existing tunnels keep passing traffic until a key expires or an endpoint changes, but you cannot add nodes, change policy or recover from a roamed endpoint. If a relay dies, only the nodes that depended on it lose connectivity. Designing for an edge fleet means asking, for each plane, what happens at 3 a.m. when it is unreachable.

The WireGuard data plane in detail

Each WireGuard peer is identified by a 32-byte Curve25519 public key. The routing table is called cryptokey routing: outbound, the destination IP selects the peer and that peer’s session encrypts the packet; inbound, after authentication and decryption, the packet’s inner source IP must be in the allowed-IPs set of the same peer, otherwise it is dropped (WireGuard whitepaper, section on cryptokey routing). This gives a one-to-one mapping between IP addresses and peers and doubles as a built-in source-address filter, which is a security property people forget when they compare it to IPsec.

The handshake is one round trip: an initiation message of 148 bytes and a response of 92 bytes, per the field layouts on the protocol page. After the handshake each transport packet carries a 16-byte header, the payload padded to a multiple of 16 bytes, and a 16-byte authentication tag. Sessions are rekeyed on a timer: the whitepaper lists REKEY_AFTER_TIME at 120 seconds, REJECT_AFTER_TIME at 180 seconds, REKEY_ATTEMPT_TIME at 90 seconds, REKEY_TIMEOUT at 5 seconds and KEEPALIVE_TIMEOUT at 10 seconds, with a message-count rekey trigger at 2^60 messages.

Two details matter at the edge. First, WireGuard is silent toward unauthenticated senders: handshake messages carry a MAC keyed by the receiver’s public key, so a port scanner gets no response, and under load the responder can demand a cookie MAC tied to the sender’s IP, with the cookie secret rotating every two minutes. Second, there is no connection state when idle. A tunnel with no traffic sends nothing, which is excellent for battery or metered links but means a NAT mapping will silently expire unless you configure persistent keepalives. The whitepaper describes the optional persistent keepalive without prescribing an interval; the common practice of 25 seconds is a convention chosen to stay below typical NAT UDP timeouts, not a protocol requirement.

Roaming comes free. If a peer has no configured endpoint, WireGuard learns it from the outer source address of the most recent correctly authenticated packet, so a gateway that changes cellular IP is rediscovered the moment it sends one valid packet. That single mechanism is why a static hub-and-spoke WireGuard design works acceptably for many fleets: spokes behind NAT dial a hub with a public IP, and the hub learns their current endpoints.

Why hub-and-spoke is not enough

The hub-and-spoke pattern is the honest baseline and I would not dismiss it. One cloud VM with a public IP, one peer entry per gateway, and PersistentKeepalive on each spoke gets you remote access with no vendor and almost no moving parts. The costs show up later. Every join edits the hub config; identity is a public key with no link to a person or a device certificate; policy is limited to AllowedIPs plus whatever firewall rules you write; and all spoke-to-spoke traffic hairpins through the hub, doubling latency and concentrating bandwidth cost there.

For a fleet where engineers reach gateways and gateways never talk to each other, hub-and-spoke remains defensible, and it is the right comparison point for the rest of this post. A control plane has to beat it on something you care about: self-service onboarding, SSO-bound identity, per-tag policy, direct paths between sites, or an audit trail.

What a control plane actually distributes

Strip away the branding and every control plane does four jobs. It authenticates a node (interactive login through an identity provider, or a pre-authorized key for unattended devices). It records the node’s WireGuard public key and assigns it an overlay address. It pushes to every authorized peer a “netmap”: the list of peers, their keys, their candidate endpoints and the allowed traffic. And it applies policy by filtering that netmap per node, so a node only learns about peers it may talk to.

That last point is subtle and valuable. In Tailscale’s model the policy engine runs in the coordination server and the filter rules are compiled and pushed to each client, which enforces them locally; a device never even receives the keys of peers it may not reach. The data path remains peer to peer. The NAT traversal article from Tailscale describes the coordination server as a side channel that tolerates seconds of latency and carries only a few thousand bytes per exchange, which is why the author notes a tiny VM can serve thousands of machines. That is a design claim by the vendor about the control channel, not a benchmark of any product.

The corollary for the edge is a good one: a control plane’s load is proportional to churn (joins, endpoint changes, policy edits), not to traffic volume. A fleet of 5,000 mostly static gateways produces little control traffic, while a fleet of phones changing networks constantly produces much more.

NAT Traversal: Where Edge Fleets Succeed or Fail

NAT traversal for a mesh VPN works in four steps: each node learns its public address by sending a STUN request from the same UDP socket it uses for tunnel traffic; the control plane exchanges those candidate addresses between peers; both peers send UDP probes at roughly the same time so each stateful firewall opens a return path; and keepalives hold the mapping open. When that fails, traffic falls back to a relay and upgrades later.

NAT traversal sequence for a mesh VPN edge: registration, relay start, simultaneous probes, direct upgrade

Figure 2: Connections start on the relay so the first packet is never lost, then upgrade to a direct path once simultaneous probes succeed.

Figure 2 follows the sequence described in Tailscale’s long-form article on how NAT traversal works. The key trick is that the relay is not a last resort tried after a timeout; it is where the connection begins, and the direct path is a background upgrade. Users see a working connection immediately, and a fraction of a second to several seconds later the path flips to direct. NetBird implements the same idea with ICE candidate gathering using the Pion ICE library, per its documentation.

Easy NAT, hard NAT, and the birthday trick

The deciding variable is the NAT’s mapping behavior. An “easy” NAT uses endpoint-independent mapping: the same internal ip:port maps to the same external port regardless of destination, so the port learned from a STUN server is also valid for the peer. A “hard” NAT uses endpoint-dependent mapping, allocating a different external port per destination, so the port STUN reported is useless for the peer (Tailscale NAT traversal article).

The pairings are what matter operationally. Easy to easy works with simple simultaneous open. Easy to hard can still work: the article describes the easy side probing random destination ports while the hard side opens roughly 256 ports, and quotes birthday-paradox odds of about 50 percent after 174 probes, 64 percent after 256, 98 percent after 1,024 and 99.9 percent after 2,048. At 100 probes per second, half of attempts succeed in under two seconds.

Hard to hard is the killer. Each random probe also picks a random source port on the hard NAT, squaring the search space. The same article calculates roughly 170,000 probes per side for 99.9 percent success (about 28 minutes at 100 per second), a median near 54,000 packets, and the risk of exhausting a router’s session table (it cites a 64,000-session limit on one Juniper SRX 300 as an example). In practice, product docs say it plainly: Tailscale’s connection-types page states that it cannot establish a direct connection when both devices are behind hard NAT, and recommends enabling NAT-PMP or UPnP, static port mapping, public addresses, or a peer relay.

Cellular and carrier-grade NAT

Cellular edge gateways are where this stops being theory. Carrier-grade NAT (CGNAT) is typically a large pool-based NAT run by the operator; whether it behaves as easy or hard varies by operator and by APN, and I am not aware of a published census I would trust, so measure your own carriers rather than assume. Two gateways on different operators’ CGNAT may be hard to hard, which means relay.

Port-mapping protocols do not rescue you. UPnP IGD, NAT-PMP and PCP ask the nearest gateway to forward a port, and the Tailscale article notes they fail under double NAT, including carrier-grade NAT, because the mapping applies only to the nearest NAT layer. For a cellular gateway the consequence is simple: design as if relay will be the common path for gateway-to-gateway traffic, and treat direct connections as a bonus.

Which brings up a useful asymmetry. Most edge fleets are not gateway-to-gateway meshes. They are engineers and cloud services reaching gateways. If the cloud-side node has a public IP with UDP open (Tailscale’s default is UDP 41641, changeable with the --port flag on tailscaled), then the “easy” side is the cloud node and traversal to a hard-NAT gateway succeeds the way a client reaches a server. The gateway initiates outbound; the mapping it creates is usable by the public node. Giving one side of every important flow a public address removes the hard-hard problem entirely.

Relays: DERP, TURN, WebSocket, and peer relays

Tailscale’s DERP (Designated Encrypted Relay for Packets) servers forward encrypted payloads over HTTPS by destination public key. They do double duty as the side channel and the fallback. Because DERP rides on HTTP, it works from networks that block almost all UDP; the NAT article cites a university guest network that allows only DNS. Tailscale’s docs describe DERP as reliable but with limited quality of service, and note that using a relay when a direct path was possible is a common source of performance complaints.

NetBird’s relay is described in its docs as a TURN server implemented with Coturn, with a note that starting in v0.29.0 a WebSocket-based relay replaces the Coturn relay. Relayed traffic stays WireGuard-encrypted, so the relay operator sees metadata (who talks to whom, volume) but not content. Both designs share a property worth stating clearly: relays are an availability and privacy-metadata concern, not a confidentiality concern, as the payload is encrypted end to end with keys the relay never holds.

A newer option is the peer relay: a node in your own network acts as the relay. Tailscale’s docs describe Peer Relay with a connection order of direct first, then peer relay, then DERP, and say it runs in your own infrastructure. For a plant with a strict egress firewall, a peer relay on a DMZ host that both sides can reach is often the difference between “works” and “ticket to IT”. Headscale’s feature checklist lists both an embedded DERP server and peer relays as supported, which I confirmed on its features page this month.

Keepalives and mapping lifetime

NAT mappings expire. The Tailscale article notes firewall UDP timeouts are often around 30 seconds, so peers send keepalives to hold the hole open. For plain WireGuard you must configure PersistentKeepalive on any peer behind NAT that needs to be reachable while idle. On a metered cellular plan this has a price: a 25-second interval is about 3,456 packets a day per tunnel, each tiny (a WireGuard keepalive is an empty transport message, 32 bytes of WireGuard overhead before IP and UDP headers). The arithmetic is trivial, around 100 KB a day plus headers, but across 10,000 SIMs it becomes a line item worth knowing.

Walk-through: Four Control Planes Compared

The four options differ in who runs the control plane, what you can inspect, how policy is expressed, and where the licensing line sits. The sections below cover each, then give a matrix. Product facts come from vendor documentation read in October 2026; pricing and limits are the kind of data that drifts, so confirm before budgeting.

Plain WireGuard with your own automation

You own everything. A configuration management tool renders wg0.conf per node from an inventory, and a hub with a public address terminates spokes. Policy is AllowedIPs plus nftables. Identity is the key you generated. The upside is zero third-party dependency and the smallest possible attack surface; the downside is that every feature people expect from a “mesh VPN” (SSO login, per-user ACLs, MagicDNS-style naming, direct spoke-to-spoke, device posture) is something you build. Here is a minimal spoke and hub pair:

# /etc/wireguard/wg0.conf on an edge gateway (spoke)
[Interface]
Address = 10.99.0.21/32
PrivateKey = <gateway-private-key>
ListenPort = 51820

[Peer]                       # the hub in the cloud
PublicKey = <hub-public-key>
Endpoint = hub.example.com:51820
AllowedIPs = 10.99.0.0/24    # overlay only, not 0.0.0.0/0
PersistentKeepalive = 25     # hold the NAT mapping open
# hub: one [Peer] block per gateway, no Endpoint needed (learned by roaming)
[Peer]
PublicKey = <gateway-21-public-key>
AllowedIPs = 10.99.0.21/32, 192.168.21.0/24   # gateway plus its OT subnet

Note the second AllowedIPs entry on the hub: that is cryptokey routing used as a subnet router. Packets for 192.168.21.0/24 are encrypted to gateway 21, and gateway 21 must have IP forwarding on. It also demonstrates the trap: two sites using the same private subnet cannot both be listed, because AllowedIPs ranges must be unique per interface.

Tailscale

Tailscale pairs open-source clients with a proprietary hosted coordination service and a global DERP relay fleet. As I understand it, the client code is published under a permissive license while the hosted control server is closed; I verified that Headscale exists precisely because the server is not open, but I did not independently re-verify the client license text this run, so check the repository before relying on it.

Policy is expressed in a tailnet policy file (HuJSON) with groups, tags, ACLs or the newer grants, auto-approvers for subnet routers, SSH rules and tests. For fleets the central object is the tag. A gateway authenticates with a tagged auth key, becomes tag:gateway, and is owned by the tag rather than by a person, so there is no human login to expire and no human to offboard. Policy then reads like intent:

{
  "tagOwners": { "tag:gateway": ["group:platform"], "tag:scada": ["group:ot-ops"] },
  "acls": [
    { "action": "accept", "src": ["tag:scada"],  "dst": ["tag:gateway:502,4840"] },
    { "action": "accept", "src": ["group:platform"], "dst": ["tag:gateway:22"] }
  ],
  "autoApprovers": { "routes": { "192.168.0.0/16": ["tag:gateway"] } },
  "tests": [
    { "src": "tag:scada", "accept": ["tag:gateway:502"], "deny": ["tag:gateway:22"] }
  ]
}

The tests block is underused and excellent: policy is checked on every save, so a typo cannot silently open SSH to the fleet. Pricing as listed on Tailscale’s pricing page in October 2026: a free Personal plan for up to 6 users, Standard at $8 per user per month, Premium at $18 per user per month, and custom Enterprise, with 50 tagged resources included on all plans and extra tagged resources at $1 per month each. Tagged resources are the unit that matters for fleets, so read that line carefully; I could not confirm from the page text which per-plan limits apply to features like SSO, so verify them for your tier.

Headscale

Headscale is an open-source (BSD-3-Clause) implementation of the Tailscale control server, written in Go, used with the official Tailscale clients. Its own README is blunt about scope: it targets a single tailnet, “suitable for a personal use, or a small open-source organisation,” and says the project does not support or encourage running behind reverse proxies or in containers. That README language is old and worth reading as a statement of project intent, not as a measurement of capacity; I found no published scale benchmark, so I will not quote a node limit. Treat any limit as something you must load-test.

Its feature checklist is broad: node registration by web auth or pre-authenticated keys, MagicDNS and split DNS, Taildrop and Taildrive, tags, subnet routers, exit nodes, route filtering, ephemeral nodes, an embedded DERP server, peer relays, ACLs, grants, auto-approvers, Tailscale SSH, node attributes and policy tests, plus OIDC registration. Listed as not supported: Funnel, Serve and network flow logs. OIDC groups cannot yet be used in ACLs. A working minimal setup:

# headscale config.yaml (excerpt)
server_url: https://hs.example.com:443
listen_addr: 0.0.0.0:8080
prefixes: { v4: 100.64.0.0/10, v6: fd7a:115c:a1e0::/48 }
derp:
  server:
    enabled: true            # embedded DERP + STUN
    stun_listen_addr: 0.0.0.0:3478
  urls: []                   # disable Tailscale public DERP map for sovereignty
policy: { mode: file, path: /etc/headscale/policy.hujson }
headscale users create plant-a
headscale preauthkeys create --user plant-a --reusable --expiration 24h --tags tag:gateway
# on the gateway (stock tailscale client):
tailscale up --login-server https://hs.example.com --authkey <key> --advertise-routes=192.168.21.0/24

Exact flag spellings can change between releases, so pin a release and read its docs rather than copying blindly. Disabling the public DERP map matters for air-gapped or sovereignty-driven fleets, but it means you now own relay capacity and its geographic placement.

NetBird

NetBird is an independent open-source stack: clients, a management service, a signal service and a relay, with an admin dashboard. Per its docs, management stores peer state and public keys and assigns private addresses from 100.64.0.0/10; signal helps peers exchange connection candidates and stores nothing; the relay is a TURN fallback with a WebSocket relay replacing Coturn from v0.29.0. Clients use kernel WireGuard where available and ICE for candidate selection, and apply access rules through the host firewall.

On licensing, the repository README states BSD-3-Clause for everything except the management, signal and relay directories, which are AGPLv3. For a company that only runs the stack internally, AGPL generally imposes obligations only when you modify and offer it over a network to others, but that is a legal call for your counsel, not a conclusion I can give you.

Self-hosting is documented as a one-script Docker Compose install: a Linux VM with at least 1 CPU and 2 GB of memory, a public domain, TCP 80 and 443 and UDP 3478 open, an embedded Dex identity provider so no external IdP is required, and optional Traefik and CrowdSec containers. Features listed in the README include setup keys for bulk provisioning, group-based access rules, device posture checks, periodic re-authentication, a Terraform provider, an Ansible collection, a public API, and platform builds for OpenWRT, MikroTik RouterOS, pfSense, OPNsense and Raspberry Pi. For fleets that provision with Terraform, that API-first stance is a real advantage.

Netmaker and ZeroTier, briefly

Netmaker is a WireGuard-based network manager with a server you operate; ZeroTier is a different design with its own protocol rather than WireGuard, using root servers for discovery and its own virtual Ethernet semantics. I did not re-verify either product’s current licensing or limits this run, so I will not compare them in detail. If you evaluate them, apply the same four questions used below: who runs the control plane, how is policy expressed, what is the relay story, and what happens when the control plane is down.

Subnet routers and OT networks

Most industrial devices cannot run a VPN client: PLCs, drives, meters, cameras. The pattern is a subnet router, a Linux box on the cell’s LAN that advertises the OT subnet into the overlay. SCADA or engineering workstations reach 192.168.10.5 through the overlay, the router forwards, and the PLC sees the router’s LAN address.

Subnet router pattern for mesh VPN edge: PLC subnet, redundant routers, overlay and ACL tags

Figure 3: Redundant subnet routers advertise an OT subnet into the overlay; a tag-based grant limits SCADA to Modbus port 502.

Tailscale’s subnet router documentation spells out the gotchas that bite in the field. IP forwarding must be enabled on the router. Advertised routes must be approved, either in the admin console or through autoApprovers, and approval is separate from access rules: a route can be approved and still carry no allowed traffic until a grant permits it. Linux clients need --accept-routes set explicitly, whereas Windows, macOS, iOS and Android pick routes up automatically. SNAT is on by default so traffic appears to come from the router, which makes OT return paths trivial; disabling it needs a return route for 100.64.0.0/10 via the router’s LAN address on the OT side, and the docs warn against combining SNAT-disabled routing with the exit-node role on the same node.

Two details deserve emphasis for fleets. First, overlapping subnets: if every site uses 192.168.1.0/24 (they will), you cannot advertise all of them. The docs note longest-prefix matching with no fallback to a less specific route when a more specific router is offline, so traffic is dropped, and they point to a separate 4via6 feature for overlapping ranges; I did not read that page, so check it. Second, key expiry: if a subnet router’s node key expires, its routes remain configured but are unreachable. Disable key expiry on infrastructure routers, or authenticate them with tagged keys, and configure high availability so a standby takes over.

NetBird documents network routes as a feature (routes to external networks, exit nodes), and Headscale’s checklist includes subnet routers and route filtering. Plain WireGuard does it with AllowedIPs plus kernel forwarding, as shown earlier. In all four cases the conceptual rule is identical: the overlay carries a route, the router terminates it, and your firewall on the router decides what the OT side may see. Do not let the overlay become a flat network into a control system. For protocol-level context on what you are exposing, see our comparison of Sparkplug B and plain MQTT topic design, and for fleet motion over the same links, VDA 5050 AMR fleet management.

Throughput and overhead reasoning

WireGuard overhead per packet is the outer IPv4 header (20 bytes) plus UDP (8) plus WireGuard’s 16-byte header plus a 16-byte tag, so 60 bytes on IPv4 and 80 on IPv6, plus padding to a 16-byte multiple. On a 1500-byte path MTU that leaves an inner MTU of 1440 (IPv4) or 1420 (IPv6) bytes; 1420 is the commonly used default, and a cellular path with a smaller MTU needs you to lower it. This is first-principles arithmetic, not a vendor claim. If a PLC protocol rides inside, an MSS clamp on the overlay interface avoids silent fragmentation stalls.

For raw speed, the useful public data comes from Tailscale’s April 2023 engineering post on wireguard-go. Using iperf3 single-stream TCP, they measured kernel WireGuard at 2.67 Gb/s on an AWS c6i.8xlarge and wireguard-go at 5.34 before and 7.32 after UDP GSO/GRO and checksum work; on a bare-metal i5-12400 with a 25G NIC, kernel WireGuard reached 11.8 Gb/s and wireguard-go 13.0 Gb/s after. That is a vendor benchmark on specific hardware, three years old, and it shows the userspace implementation can match or beat the kernel module under favorable conditions. Do not extrapolate it to an ARM gateway with a 1 GHz core.

For edge hardware the more honest statement is: the bottleneck is CPU for ChaCha20-Poly1305 (which is fast on cores without AES instructions) and the packet rate, not the protocol. Measure with iperf3 on your actual gateway, in both direct and relayed modes, because the relayed path adds a hop and DERP is documented as having limited quality of service.

Decision matrix

Criterion Plain WireGuard Tailscale Headscale NetBird
Control plane None (your automation) Hosted by vendor Self-hosted, open source Cloud or self-hosted
Clients wg / wireguard-go Tailscale clients Tailscale clients NetBird clients
NAT traversal Manual (port + keepalive) STUN, probes, DERP, peer relay Same clients; embedded DERP ICE, TURN or WebSocket relay
Policy AllowedIPs + firewall HuJSON ACLs, grants, tags, tests Same policy model, partial parity Groups, access rules, posture checks
Multi-tenant n/a Per-tailnet Single tailnet by design Multi-account in product
License GPLv2 kernel code Proprietary service BSD-3-Clause BSD-3 clients, AGPLv3 server parts
Best fit Small, static, hub-and-spoke Fast rollout, small ops team Sovereignty, one org Self-hosted with UI and API

The WireGuard license cell reflects my knowledge that the Linux kernel module is GPLv2; I did not re-check it this run. The other cells come from the sources cited above.

Trade-offs, Gotchas, and What Goes Wrong

Decision flow for WireGuard vs Tailscale vs Headscale vs NetBird in edge fleets

Figure 4: A decision flow for choosing between a SaaS control plane, Headscale, NetBird or plain WireGuard.

Figure 4 compresses the choice into three questions, but the failure modes decide whether the choice survives contact with production.

The control plane is a single point of change, not of traffic. When it is down, established tunnels persist but nothing new joins, nothing re-resolves after endpoint changes, and expiring keys strand nodes. If you self-host Headscale or NetBird, you own that availability: back up the database, test restore, and monitor certificate expiry. Headscale’s own README warns that main may contain unreleased changes and advises matching docs to a released tag, so pin releases and rehearse upgrades on a staging instance.

Key expiry strands unattended devices. Interactive logins expire by design. A gateway enrolled with a person’s login will drop off the network months later, usually on a weekend. Use tagged or pre-authorized keys for machines, disable expiry on infrastructure routers, and alert on nodes that have not checked in.

Relay dependence hides as latency. A fleet that works but runs entirely through relays shows high round-trip times and low throughput, with no error. Check tailscale status (it shows direct, relay or peer-relay) and tailscale ping; NetBird exposes connection type in its status output. Alert when a node you expect to be direct is relayed for hours.

Port mapping and double NAT. UPnP and NAT-PMP are unavailable or disabled on most industrial routers and fail under CGNAT. Do not build a plan on them. Put a public, UDP-reachable node on one side of each flow, or deploy a peer relay inside the plant DMZ.

Overlay addressing collides. Tailscale and NetBird both allocate from 100.64.0.0/10, the CGNAT shared address space. Some cellular carriers and some existing VPNs also use ranges in or near it, and overlapping routes break reachability in confusing ways. Audit before rollout.

Security scope creep. A mesh VPN authenticates machines and users to the overlay; it does not replace segmentation inside the OT zone. An engineer’s laptop with a broad ACL becomes a bridge into the control network. Write least-privilege grants by tag and port, test them, and log access. If your workloads authenticate to each other, layer identity such as SPIFFE on top rather than treating overlay membership as trust.

Vendor and licensing drift. Tailscale’s hosted plans changed shape (the pricing page now lists Standard where older Starter plans exist, with a note that legacy plans migrate). Headscale tracks a moving Tailscale client protocol, so a client upgrade can outpace the server; NetBird’s relay implementation changed from Coturn to WebSocket across versions. None of this is a flaw, but it argues for pinned versions and canary rollouts across a fleet.

OTA coupling. A VPN that fails to come up after a firmware update is a bricked remote site. Stage the VPN agent with the same safety net as your OS image; our comparison of SUIT, Uptane, Mender, RAUC and SWUpdate covers rollback designs that make this survivable.

Practical Recommendations

For a fleet under roughly a few hundred devices with a small operations team and no sovereignty constraint, start with Tailscale: tagged auth keys, a policy file with tests, subnet routers where PLCs need reaching, and a peer relay or two in plant DMZs. The operational surface you do not run is the main value, and the per-user pricing is modest when most identities are tags and a handful of engineers. Re-check the pricing page for the tagged-resource line before committing.

If data residency, air-gap or cost at scale rules out the hosted service and you have one organization, evaluate Headscale with the official clients, but treat its single-tailnet design intent as a hard boundary and load test your expected node count, because I found no published capacity figure. If you need multiple tenants, a built-in dashboard, posture checks and a Terraform provider in a self-hosted stack, NetBird is the more natural fit, with the AGPL components reviewed by legal first.

If your topology is genuinely hub-and-spoke and the fleet is stable, plain WireGuard plus automation remains the least risky choice.

  • Decide per flow who initiates and give one side a public UDP-reachable address.
  • Use tags or setup keys for machines; never enroll unattended devices with personal logins.
  • Disable key expiry on subnet routers; deploy two per site for failover.
  • Write policy as code, with tests, in version control.
  • Monitor connection type (direct versus relayed) and last-seen time per node.
  • Set the overlay MTU deliberately (1420 is a common default) and clamp TCP MSS.
  • Pin client and server versions, canary rollouts, and keep a rollback path for the VPN agent.
  • Measure throughput on target hardware in both direct and relayed modes.

Frequently Asked Questions

Is Tailscale just WireGuard?

Tailscale uses WireGuard for the encrypted tunnels but adds what WireGuard omits: a coordination server that distributes keys and policy, identity provider login, NAT traversal with STUN and hole punching, DERP relays for fallback, MagicDNS, subnet routers and an ACL language. The data plane is WireGuard; the product is the control plane around it. That is why the comparison with raw WireGuard is really about operations and identity, not about packet encryption or speed.

Can I replace the Tailscale coordination server with Headscale?

Yes, Headscale is an open-source implementation of the Tailscale control server that works with the official clients, and its features page lists ACLs, subnet routers, exit nodes, an embedded DERP server and OIDC registration. Its README scopes it to a single tailnet for personal or small-organization use, and some features such as Funnel, Serve and network flow logs are not supported. Load test it at your scale before committing.

NetBird or Headscale for a self-hosted edge fleet?

Choose by what you need around the tunnel. Headscale keeps the Tailscale clients and policy model, ideal if you want that ecosystem without the hosted service for one organization. NetBird ships its own clients, a dashboard, setup keys, posture checks, and a documented Docker Compose installation with an embedded identity provider. NetBird’s management, signal and relay code is AGPLv3, which your legal team should review. Pilot both on a dozen devices.

What happens when two edge devices are both behind CGNAT?

Direct connection often fails if both NATs are hard (endpoint-dependent mapping), because neither side can predict the other’s external port. Tailscale’s documentation says it cannot establish a direct connection when both devices are behind hard NAT; traffic then uses a relay such as DERP or a peer relay. The practical fix is to route through a node with a public UDP-reachable address or to run a peer relay inside infrastructure both sides can reach.

Does a mesh VPN add noticeable latency or reduce throughput?

A direct WireGuard path adds a small per-packet cost, 60 to 80 bytes of header overhead on a 1500-byte path and encryption CPU time. A relayed path adds a hop and, for DERP, is described by Tailscale as having limited quality of service. Public data shows wireguard-go reaching 13 Gb/s on a 25G bare-metal host, but on low-power gateways the CPU is the limit, so measure on your actual hardware in both modes.

Do I still need a firewall inside the overlay?

Yes. A mesh VPN controls which nodes can reach which, but it is not segmentation inside an industrial zone. Use tag-based ACLs by port, keep the subnet router’s own firewall tight, avoid advertising broad ranges, and log access. Treat overlay membership as one authentication factor, not a trust boundary, and add workload-level identity where services authenticate each other.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *