Mesh VPNs in 2026: revisit Headscale, NetBird and plain WireGuard before you open another SSH port to the internet
Take a pump manufacturer in Coimbatore that makes agricultural and domestic pumps at two plants, one near the city and a smaller one at Kaniyur (an invented example, not a real client). The company has about 60 office and plant laptops, an ERP server and a file server in a small server room at the main plant, a CNC programming PC on each shop floor, a monitoring VM at a cloud provider, and a field service team of about 25 technicians who visit farms, apartment complexes and dealers across Tamil Nadu and Kerala with laptops and phones. A dealer portal runs in the cloud. The IT team is two people, and one of them is also the person who orders printer toner.
Over the years, remote access grew one hole at a time. Port 22 on the ERP server was forwarded on the plant router so a consultant in Bengaluru could fix reports. RDP on the accounts PC was opened “for one week” during an audit and stayed open. The field team reached the service ticket system through a shared login on a public URL. When the router logs were finally read, they showed thousands of failed SSH logins a day from addresses nobody recognised. Nothing had been broken into, as far as anyone could tell, but nobody could say that with confidence either.
This post compares three ways to replace those open ports with a private network that only your own devices can join, as of 25 October 2026: Headscale (an open source, self-hosted control server for Tailscale clients), NetBird (self-hosted), and plain WireGuard with wg-quick and static configuration files. Tailscale’s own design is used as context, because it explains the words everyone else uses: control plane, data plane, DERP relays and NAT traversal. Nebula and Firezone get a short note.
The facts come from primary material: project READMEs, documentation, release notes and licence files, GitHub release and advisory data, and NVD and OSV records. Release data was re-checked through the GitHub API on the morning of 25 October 2026. No hands-on drill was run for this post; see the short note near the end. Where I could not find a primary source, I say so. What I say about licences is my own reading, not legal advice.
The short version
- The problem is not encryption, it is distribution. WireGuard already does the cryptography well. What it does not do is hand out keys, addresses and rules to dozens of moving laptops, or get two devices behind NAT to talk. That job is the control plane, and it is the real thing you are choosing.
- Headscale 0.29.6 gives you the Tailscale clients with your own control server. You get the polished Tailscale apps, MagicDNS, subnet routers, exit nodes, ACLs and grants, and Tailscale SSH, with a single Go binary and SQLite. Its own documentation says it targets a single tailnet for personal use or a small organisation, is not enterprise software, and does not officially support Docker or reverse proxies.
- NetBird 0.81.4 gives you the whole stack under your control. Client, Management, Signal and Relay are all open source, with a web dashboard, SSO, setup keys, groups, posture checks and routing. The self-hosted path is a Docker Compose install. The client is BSD-3-Clause; Management, Signal, Relay and the combined server are AGPLv3. It releases very often, and it has had a run of security advisories in 2026, all with fixed versions.
- Plain WireGuard is still the right answer for a few fixed links. Two plants and a cloud VM that never move can be joined with three small files and no extra server. It stops being simple when people, laptops and phones join and leave every month.
- Whatever you pick, the control server becomes your most sensitive server. Whoever controls it decides which keys are trusted and who can reach what. Patch it, back it up, put SSO in front of it, and keep its admin interface off the open internet.
Where things stand on 25 October 2026
All times are Indian Standard Time; GitHub gives release stamps in UTC, and I converted them.
Headscale
- Latest: 0.29.6 (24 October 2026, 3:59 PM). It is built with Go 1.27.3 to reject certificate chains with the compromised eMudhra intermediate keys, updates
golang.org/x/net,golang.org/x/textand gRPC, and speeds up peer-map construction on large tailnets. - Previous: 0.29.5 (13 October 2026, 6:35 PM). The 0.29.0 release came on 17 June 2026 at 2:51 PM, after four betas starting on 22 May. It added SSH rules with the
checkaction and beta policytestsandsshTestsblocks. The previous minor, 0.28.0, came on 5 February 2026. - Client support: the minimum supported Tailscale client version for 0.29.x is 1.80.0, and the project aims to support the last 10 releases of the Tailscale client.
- Upgrades: the FAQ says you must go from one minor version to the next without skipping (for example 0.26 to 0.27 to 0.28), always picking the latest patch.
- Licence: BSD-3-Clause. The project describes itself as “Open Source, acknowledged contribution”: changes are discussed with the maintainers before a pull request.
NetBird
- Latest: 0.81.4 (24 October 2026, 4:54 PM). Its notes list distrusting the compromised eMudhra CA keys and an SSH privilege fix for PKCS#11 package builds.
- Recent line: 0.81.0 (15 October 2026, 7:35 PM), then 0.81.1 and 0.81.2 on 16 October and 0.81.3 on 19 October. Of the 100 most recent GitHub releases fetched on 21 October, 85 were stable releases dated 2026, plus release candidates and canary builds. This is a project that ships several times a week; plan for that.
- Licence, checked in the repository’s LICENSE file on 25 October 2026: the file says BSD-3-Clause applies to all parts of the repository except the directories
management/,signal/,relay/andcombined/, which are licensed under AGPLv3, with their own LICENSE files. ALICENSESfolder holds both texts. GitHub shows the repository licence as “NOASSERTION” because it is mixed; that label does not mean there is no licence. The repository also carries a contributor licence agreement file.
Plain WireGuard
- WireGuard has been in the Linux kernel since 5.6 (March 2020), and
wireguard-toolsprovideswgandwg-quick. The Go userspace implementation,wireguard-go, is mirrored on GitHub with no GitHub releases; its official repository is on git.zx2c4.com. For plain WireGuard, “the version” that matters is usually your distribution’s kernel and its security updates.
Tailscale and others, for context
- Tailscale client: 1.104.2 (21 October 2026, 8:25 AM). Headscale works with these same clients.
- Nebula (MIT): 1.11.2 on 22 September 2026. Firezone (Apache-2.0): gateway 1.6.3 on 14 October 2026, from the 21 October check.
First question: who must reach what, and from where?
Before choosing a tool, write down the flows. For the Coimbatore company, the list looks like this:
- Field technicians, from mobile data and customer Wi-Fi, to the service ticket system and the ERP’s service module.
- The two IT staff, from anywhere, to SSH on servers and RDP on a few PCs.
- The Kaniyur plant to the ERP and file server at the main plant, all day.
- The cloud monitoring VM to every server, for checks and metrics.
- The outside consultant, a few hours a month, to SSH on the ERP server only.
Three things fall out. Most flows are people to services, and the people move; that is where a control plane with SSO earns its keep. Flow 3 is site to site and never moves; a fixed tunnel is enough. And nobody needs to reach a laptop, so the network needs a mesh for reachability and a policy for permission, not everything talking to everything.
Also ask: which devices cannot run an agent? The CNC programming PCs run an old Windows build the machine vendor will not let you touch, and the plant PLCs run nothing at all. Those are reached through a subnet router, a device on the mesh that forwards traffic to a local network, not by installing a client on them.
What each one actually is
How Tailscale works, as context
Tailscale splits the job in two. The data plane is WireGuard: each device has its own key pair and talks to other devices over encrypted tunnels, directly where possible. The control plane is a coordination server that Tailscale describes as essentially a shared drop box for public keys: devices log in, upload their public key, and receive the list of peers, their keys, addresses, DNS settings and the policy. In Tailscale’s own words, the control plane is hub and spoke but carries virtually no traffic; the data plane is a mesh.
The hard part is getting two devices behind NAT and firewalls to talk directly. Tailscale’s NAT traversal article explains the idea: learn your public address with STUN, exchange candidates, and send packets from both sides at once so each firewall lets the reply in. When that fails, traffic falls back to a relay. Tailscale’s relays are DERP servers (Designated Encrypted Relay for Packets). Its documentation says most connections only use DERP to set up a direct connection, that it first tries peer relays (other devices in your tailnet) before DERP when those are configured, and that DERP cannot decrypt traffic because private keys never leave the device that generated them.
All three tools here answer the same question differently: who runs the drop box for keys, and who runs the relay?
Headscale 0.29.6
Headscale is an open source, self-hosted implementation of the Tailscale control server. You run one Go binary, usually as a systemd service with a dedicated headscale user, configuration in /etc/headscale/config.yaml, and SQLite under /var/lib/headscale. Devices run the normal Tailscale clients, pointed at your server with --login-server.
Its features page lists registration by web login, pre-auth keys or OpenID Connect; MagicDNS with split DNS; Taildrop and Taildrive; tags; subnet routers and exit nodes with auto-approvers; ephemeral nodes; an embedded DERP server; peer relays; ACLs, grants and Tailscale SSH. Not supported: Funnel, Serve and network flow logs. OIDC groups cannot be used in ACLs, so groups in the policy are written out by hand.
The project is honest about scope. It implements a single tailnet. Its FAQ says performance is not a primary design goal: each change makes Headscale recompute a “world map” for every node, so many nodes that change often (laptops moving between home and office) keep the CPU busy, and in the worst case nodes stop learning the current state. It says Headscale can likely handle hundreds of devices (maybe more) if there is little change.
Requirements are modest: a server with a public IP (dual stack recommended), HTTPS on TCP 443, TCP 80 only for the built-in Let’s Encrypt HTTP-01 challenge, UDP 3478 if you enable the embedded DERP server, and the metrics port (TCP 9090) kept private. The documentation strongly recommends HTTPS on 443 because the Tailscale client assumes it in some situations.
NetBird 0.81.4
NetBird is a complete, self-hostable platform with four parts. The client runs on each peer and sets up WireGuard tunnels (the wt0 interface on Linux and Windows). The Management service keeps the network state, peers’ public keys, IP addresses, groups and policies, authenticates peers, and serves the dashboard. Signal helps peers exchange connection candidates; it stores nothing, its messages are encrypted end to end between peers, and it steps out after a connection is up. Relay carries traffic when a direct connection is not possible; since 0.29.0 a WebSocket-based relay is replacing the earlier Coturn TURN relay. Traffic through the relay stays WireGuard-encrypted.
The self-hosting quickstart asks for a Linux VM with at least 1 CPU and 2 GB of memory, public TCP 80 and 443 and UDP 3478, a domain name, and Docker with the Compose plugin. A script generates the setup and asks which reverse proxy to use, with a bundled Traefik as the default. Clients need no inbound ports.
Features that matter for a company: SSO login; setup keys (one-off or reusable, with usage limits and an expiry the dialog pre-fills at 7 days); ephemeral peers, removed after 10 minutes offline; groups and access policies; peer session expiration, 24 hours by default for SSO-added peers, configurable from one hour to 180 days; posture checks on client version, country, network range, OS version, running processes and a certificate from your own CA; and routes to subnets that cannot run a client. The management service sends anonymous usage metrics by default; the FAQ documents the flag --disable-anonymous-metrics=true to turn it off.
Plain WireGuard with wg-quick
WireGuard itself is a small protocol and kernel module. Each peer has a key pair; each [Peer] section lists a public key, an optional endpoint, and AllowedIPs, which acts as both a routing table and an access list (WireGuard calls this cryptokey routing). wg-quick reads a file such as /etc/wireguard/wg0.conf, creates the interface, sets addresses, adds routes for AllowedIPs (the Table setting controls this), sets DNS through resolvconf, and runs PostUp and PostDown hooks. PersistentKeepalive keeps a NAT mapping open for a peer behind NAT.
What plain WireGuard does not do is everything a control plane does: no key distribution, no login, no expiry, no NAT traversal between two peers that are both behind NAT, no relay, no DNS for peer names, and no central policy. The known-limitations page adds that it has no TCP mode and does not try to hide from deep packet inspection. Tailscale’s “how it works” article puts the scaling problem simply: fully connecting 10 nodes means each node knows 9 peers, 90 tunnel endpoints in total, and adding a user means touching every server.
Side by side
Licence and governance
- Headscale: BSD-3-Clause, one repository, a small maintainer team with an “acknowledged contribution” model. Independent of Tailscale Inc., though it follows Tailscale’s client protocol.
- NetBird: mixed. BSD-3-Clause for the client and most of the repository; AGPLv3 for
management/,signal/,relay/andcombined/. Backed by NetBird GmbH, which also sells NetBird Cloud. If you modify the server parts and offer them to others over a network, read the AGPL carefully. - Plain WireGuard: the Linux kernel module is GPLv2 as part of the kernel;
wireguard-gois MIT. There is no company in the middle of your network. - Tailscale clients used with Headscale: the Headscale README notes that the Tailscale client is open source except the GUI clients for proprietary platforms (Windows, macOS and iOS). Tailscale’s open source page says the client is mostly open source.
Architecture and where state lives
- Headscale: one process, SQLite (PostgreSQL is still supported but, in the FAQ’s words, in “maintenance mode”), private keys and the policy file under
/var/lib/headscaleor a path you set. Backup is a copy of that directory and the config. - NetBird: several services in containers (management with its store, signal, relay, dashboard, a reverse proxy, and an identity provider or the built-in one). More moving parts, more to back up, more to upgrade together.
- Plain WireGuard: state lives in each node’s
/etc/wireguard/*.conf. There is no central server, which is both its strength and its weakness: nothing to compromise in the middle, and nothing to fix in one place.
NAT traversal and relays
- Headscale: uses the Tailscale client’s NAT traversal. By default, clients still use Tailscale Inc.’s free public DERP servers. You can enable the embedded DERP server and, if you want, remove Tailscale’s DERP map with
urls: []; the documentation warns that a single DERP server is then a single point of failure. - NetBird: ICE-based candidate exchange through Signal, with its own relay. Everything can be self-hosted; nothing depends on a third party’s relay unless you choose NetBird Cloud.
- Plain WireGuard: at least one side of each tunnel needs a reachable address and an open UDP port. Two peers both behind NAT cannot reach each other without a third machine to route through, which makes it hub and spoke in practice.
Identity and login
- Headscale: OIDC with PKCE, authorisation by domain, email or group membership at login time; pre-auth keys for servers. OIDC groups are not usable inside ACLs.
- NetBird: SSO is central to the design; user and group sync depends on the identity provider integration. Setup keys for machines; peer session expiration for people.
- Plain WireGuard: a key file is the identity. Whoever has the private key is that peer, until you remove the public key from every other config.
If you already run Keycloak or another identity provider, both Headscale and NetBird can use it. My earlier post on Keycloak and its alternatives covers that choice.
Access policy
- Headscale: a HuJSON policy in Tailscale’s format (groups, tags, tag owners, ACLs, grants, SSH rules, auto-approvers), stored in a file or the database. Since 0.29.0,
testsandsshTestsblocks reject a policy that breaks a stated expectation. This is a real strength: the policy is a text file you can review in Git. - NetBird: groups and policies in the dashboard or API, plus posture checks. Easy for a small IT team to read; harder to review as code unless you drive the API with Terraform or scripts.
- Plain WireGuard:
AllowedIPslimits which source addresses a peer may use, but it is not a service-level firewall. You still neednftablesor host firewall rules for “consultant may reach port 22 on the ERP server only”.
Clients and platforms
- Headscale: the Tailscale apps on all main platforms; some need extra configuration to use a custom server. Clients update on Tailscale’s schedule, so check the minimum supported version on each Headscale upgrade.
- NetBird: its own clients for the main desktop and mobile platforms.
- Plain WireGuard: official apps on all main platforms, and the kernel module on Linux. Very stable, very few features.
Operations and upgrades
- Headscale: few releases, but no skipping minor versions; Docker and reverse proxies are not officially supported, though community guides exist.
- NetBird: frequent releases, so pin versions, read notes weekly, and upgrade server and clients in a known order.
- Plain WireGuard: upgrades come with the operating system; the work is key rotation and config changes, ideally with Ansible.
Scale
- Headscale: its own FAQ sets the expectation: hundreds of devices if things change little, struggles when many devices change often. 85 to 100 devices with laptops and phones is in the comfortable range for most people, but test it with your own churn.
- NetBird: the project positions it for organisations. I did not find a documented, tested device count for a single self-hosted server; size by measuring.
- Plain WireGuard: the protocol scales fine; the people managing files do not.
Worked example for the Coimbatore scenario
The design, in plain words: close every inbound port except what the control server needs; put people and servers on a mesh; keep the plant-to-plant link as a fixed tunnel; give the consultant one rule, not a network.
Option A: Headscale
A small VM at a cloud provider runs Headscale on HTTPS 443, with the embedded DERP server on UDP 3478, and OIDC pointed at the company’s identity provider. Servers join with tagged pre-auth keys; people log in through SSO.
# /etc/headscale/config.yaml (excerpt, invented names)
server_url: https://vpn.pumps.example.in
listen_addr: 0.0.0.0:443
metrics_listen_addr: 127.0.0.1:9090
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
derp:
server:
enabled: true
ipv4: 203.0.113.10
# keep Tailscale's public DERP map as a fallback until your own relay is proven
dns:
magic_dns: true
base_domain: mesh.pumps.example.in
policy:
mode: file
path: /etc/headscale/policy.hujson
// /etc/headscale/policy.hujson (invented users and tags)
{
"groups": {
"group:it": ["selvi@", "arun@"],
"group:field": ["tech01@", "tech02@"],
"group:vendor": ["consultant@"]
},
"tagOwners": {
"tag:erp": ["group:it"],
"tag:ticket": ["group:it"],
"tag:router": ["group:it"]
},
"acls": [
{ "action": "accept", "src": ["group:it"], "dst": ["*:22", "*:3389"] },
{ "action": "accept", "src": ["group:field"], "dst": ["tag:ticket:443", "tag:erp:443"] },
{ "action": "accept", "src": ["group:vendor"], "dst": ["tag:erp:22"] }
],
"tests": [
{ "src": "consultant@", "accept": ["tag:erp:22"], "deny": ["tag:ticket:443"] }
]
}
A Linux box at each plant runs the Tailscale client as a subnet router for the shop-floor network, so the CNC PCs and PLCs are reachable without an agent, and only by the IT group if you write the rule that way. Because the policy has a tests block, a later edit that accidentally gives the consultant the ticket system is rejected before it applies.
Option B: NetBird
The same VM, with at least 2 GB of memory, runs NetBird’s Docker Compose stack. The IT team creates groups (IT, Field, Vendor, ERP, Ticket, Plant routers) and policies between them, a setup key limited to the number of servers with a short expiry, and a route through a plant router peer for each shop-floor subnet. Session expiration stays on for people, and a posture check sets a minimum client version, useful given the advisories below. The consultant gets port 22 to the ERP group only, removed when the contract ends.
Option C: plain WireGuard, for the fixed link only
The link between the two plants does not move, so a static tunnel is fine. The main plant has a static public IP; Kaniyur does not, so Kaniyur keeps the NAT mapping alive.
# Kaniyur plant router: /etc/wireguard/wg0.conf (keys and addresses invented)
[Interface]
Address = 10.99.0.2/30
PrivateKey = <kaniyur-private-key>
[Peer]
# main plant
PublicKey = <main-plant-public-key>
Endpoint = 198.51.100.20:51820
AllowedIPs = 10.99.0.1/32, 10.10.0.0/24
PersistentKeepalive = 25
The main plant side lists Kaniyur’s public key with AllowedIPs = 10.99.0.2/32, 10.20.0.0/24 and no endpoint, and its firewall opens UDP 51820 from anywhere (the WireGuard protocol page says the server does not respond at all to an unauthorised client, so the port stays silent to scanners) while nftables limits which plant subnets may reach which servers. This is three short files and no extra server. You could also make this link part of the Headscale or NetBird mesh; the point is that you do not have to.
What changes on the router
After the mesh is up and tested, the router loses its forwards for 22 and 3389. The only inbound ports left are on the control server VM (443, 80 if used for certificates, 3478) and, for option C, one UDP port at the main plant. If you also want to know when the control server is unreachable from outside, add it to your uptime checks; the uptime monitoring post covers how.
Security and hygiene
The control server is now your crown jewel
Except with plain WireGuard, the control server decides which keys belong to the network and what each can reach. An attacker who controls it can add a node and grant it access, and data plane encryption does not help, because the control plane tells devices whom to trust. Treat it like your identity provider: patched, backed up, minimal, admin interfaces private, logs watched.
Notable advisories
NetBird has published nine GitHub security advisories, most of them in July to September 2026. With the fixed version for each:
- GHSA-qcpp-8vwj-hhwr (CVE-2026-115576), high, 8.8: local privilege escalation through an unauthenticated IPC socket, affecting 0.5.0 to before 0.76.0.
- GHSA-28q5-j9qm-gx35, medium, 5.5: arbitrary root file read through the unauthenticated daemon socket, 0.75.0 to before 0.76.1.
- GHSA-xmg2-v32j-x2hf (CVE-2026-105429), high: SSH config
Matchline injection through a peer’s FQDN or hostname, 0.60.0 to before 0.74.7. - GHSA-7wwh-vfq4-v488 (CVE-2026-105428), medium: the netstack SOCKS5 proxy bound to all interfaces, exposing an unauthenticated proxy into the overlay, up to 0.74.6, fixed in 0.74.7.
- GHSA-jxxc-7jxf-v8hm, high: the SSH server reached vulnerable
x/crypto/sshpaths, 0.20.8 to before 0.74.4. - GHSA-v5w2-pqxj-6r94 (CVE-2026-105430), medium: the relay server decoded unauthenticated handshake bytes before verifying the HMAC signature, 0.29.0 to before 0.75.0.
- GHSA-fhw6-5mff-2gfh, high, 7.5: relay WebSocket listener leaked goroutines on idle unauthenticated connections, a denial of service, before 0.68.0.
- GHSA-rxmp-8h9v-56cx, medium, 4.4: a race in the management
UpdateUserfunction allowed an admin to become owner, up to 0.65.2, fixed in 0.65.3. - GHSA-4g2w-p87c-hxch, high, 7.8: local privilege escalation through MSI repair on Windows, 0.23.0 to before 0.49.0.
In short: any NetBird client or server older than 0.76.1 is affected by at least one of these. 0.81.4 is past all of them. Many advisories in a short time often means outside review is finding and fixing issues; it also means the update routine is not optional.
Headscale: no GitHub security advisories are listed on the repository. OSV and NVD record CVE-2023-47390 (GHSA-wp76-cf2j-rqq7): Headscale up to 0.22.3 wrote bearer tokens to info-level logs. Separately, CVE-2026-46484 affects Headplane, a third-party web UI for Headscale: a path traversal and authorisation bypass in its Headscale API client, fixed in Headplane 0.6.3 and 0.7.0-beta.3. If you add a web UI, it carries your Headscale API key, so it becomes part of your attack surface.
Tailscale clients: the GitHub advisories are old: CVE-2022-41924 (critical, remote code execution through CSRF on the Windows daemon) and CVE-2022-41925 (information disclosure through CSRF), both fixed in 1.32.3, and CVE-2023-28436 (FreeBSD Tailscale SSH group ID) fixed in 1.38.2. Tailscale also publishes its own security bulletins page; with Headscale, you still need to update clients when Tailscale fixes something.
Nebula: GHSA-69x3-g4r3-p962 (CVE-2026-25793), high: a blocklist bypass through ECDSA signature malleability, affecting 1.7.0 to 1.10.2, fixed in 1.10.3.
WireGuard: the 2026 NVD keyword hits I reviewed were in other products that implement or manage WireGuard (Zephyr, Netmaker, wg-portal) or unrelated kernel code, not in the Linux module or wireguard-go.
The eMudhra CA distrust: both Headscale 0.29.6 and NetBird 0.81.4 shipped on 24 October 2026 with changes to stop trusting compromised eMudhra CA keys. If your control server’s certificate chains to those keys, it will be rejected after the update; worth a quick check.
Habits that matter more than the version number
- Keep the admin API and dashboard off the public internet, or behind SSO and an allow list. The client endpoint must be public; the admin surface need not be.
- Use short-lived, single-use or limited-use keys for servers. Revoking a NetBird setup key does not disconnect machines already joined with it, as its documentation notes, so remove the peers too.
- Set expiry for people. Tailscale’s key expiry defaults to 180 days in its own service; NetBird’s peer session expiration defaults to 24 hours for SSO peers. In Headscale, check how node expiry is configured in your version.
- Do not leave a default “allow all” policy in place after the pilot. Start from deny, write rules per flow, and test them.
- Disable or restrict built-in SSH features unless you need them; several of the NetBird advisories above were in SSH-related code.
- Decide deliberately whether a third party’s relay may see traffic metadata (who talks to whom, how much).
- If the control server uses a public certificate, automate it; the TLS certificate automation post covers the options. An expired certificate on the control server means no new logins.
Decision guide
Lean towards Headscale if
- You want the Tailscale client experience and features (MagicDNS, subnet routers, exit nodes, Tailscale SSH) without a SaaS control plane.
- You have one network, up to a few hundred devices, and are comfortable with a single binary, a config file and a policy file in Git.
- You like policy as code with tests that block a bad change.
- You accept that the project is maintained by a small team for self-hosters, with no official Docker or reverse proxy support, and that clients follow Tailscale’s schedule.
Lean towards NetBird if
- You want every component self-hosted, including the relay and signalling, under open source licences.
- A web dashboard with groups, policies, setup keys, posture checks and session expiry suits your team better than a policy file.
- You can run a Docker Compose stack and keep up with frequent releases, and you are fine with the AGPLv3 on the server parts.
- You may later want the option of the vendor’s managed service, NetBird Cloud.
Lean towards plain WireGuard if
- The peers are a handful of fixed sites or servers with known addresses, and they rarely change.
- You want no control server at all, and you are willing to manage keys and firewall rules yourself, ideally with Ansible or similar.
- You need a simple, auditable link with the fewest moving parts.
Look at other options if
- Nebula: you prefer a certificate-based model, where a certificate states a node’s IP, name and groups, and “lighthouses” help peers find each other with UDP hole punching. You run your own small CA; there is no central login service.
- Firezone: you want a managed, policy-driven access product built on WireGuard with gateways in your network. Its README says the licences permit self-hosting but production self-hosting is not officially supported, and published clients are only guaranteed to work with the managed service.
- Tailscale’s own service: you are fine with a SaaS control plane, and want features Headscale lacks (Funnel, Serve, flow logs, multiple tailnets) without running anything.
Checklist
- Every remote flow is written down as “who, from where, to which service and port”, and the network is designed from that list.
- The router forwards no SSH, RDP or database ports; a scan from outside confirms it.
- The control server exposes only what clients need; its admin API, dashboard and metrics port are not public.
- People log in through SSO; servers join with limited-use, expiring keys; contractors have a dated end.
- The access policy starts from deny, and it is reviewed like code (with Headscale
tests, or a written list of expected flows for NetBird). - Devices that cannot run an agent are reached through a subnet router, and only by the groups that need them.
- You know whether your clients use a public relay, and you have chosen that deliberately.
- The control server’s data is backed up, and a restore has been tried once on a spare VM.
- Server and client versions are pinned and tracked, with someone reading release notes and advisories every week or two.
- You have a break-glass path (console access through the cloud provider, or a person on site) for when the control server is down.
Common mistakes
- Installing a mesh VPN and leaving the old port forwards in place “just in case”. Now there are two doors.
- Keeping a default allow-all policy after the pilot, so every laptop can reach every server and every other laptop.
- Treating the control server like any other VM: no backups, no patches, admin UI open to the internet.
- Using one reusable setup key or pre-auth key with no expiry for everything, and pasting it into a wiki.
- Removing Tailscale’s public DERP map on day one with a single self-hosted DERP server, and then finding that field laptops on some mobile networks cannot connect when it goes down.
- Upgrading Headscale across several minor versions at once, against its upgrade guide.
- Letting NetBird clients on field laptops drift several releases behind the server, past the fixes listed above.
- Building a full mesh of plain WireGuard configs for 80 devices by hand and calling it simple.
- Running the Headscale server as a node inside the same tailnet; its FAQ says this can cause problems with subnet routers, relays and MagicDNS and is not supported.
- Forgetting that the consultant’s laptop is now inside your network. Scope the rule to one host and one port, and remove it when the work ends.
Drill note
No hands-on drill was run for this post. Nothing was installed, and no Headscale, NetBird or WireGuard server was started on the box. Every statement here comes from the projects’ documentation, release notes, licence files and security records listed in the sources, checked on 21 and 25 October 2026. Before you roll out any of these, run your own pilot with two or three devices on the networks your people actually use, including mobile data, and test what happens when the control server and the relay are down.
Unlearn
- Unlearn that an open SSH port with a strong password is “fine for now”. It is a door you must guard every day.
- Unlearn that a VPN means everyone inside can reach everything. Reachability and permission are different things.
- Unlearn that “WireGuard” is the product. WireGuard is the tunnel; the product is how keys, addresses and rules are handed out.
- Unlearn that self-hosting the control plane makes the network safe by itself. It moves the trust to a server you must now look after.
Re-learn
- Re-learn the two planes: a control plane that hands out keys and policy, and a data plane that carries encrypted packets, directly where possible and through a relay when not.
- Re-learn access as a list of flows, each with a person or tag, a service and a port.
- Re-learn the update routine for network software: pinned versions, release notes, advisories, and clients kept in step with the server.
- Re-learn simple tools for simple jobs: a static WireGuard tunnel for a fixed link is still a good design.
Closing
Closing open ports is one of the cheapest security improvements a small company can make, and a mesh VPN does it without making remote work painful. Headscale 0.29.6 gives you the Tailscale clients with a control server you own, within a clearly stated small-team scope. NetBird 0.81.4 gives you the whole stack, self-hosted and open source, with more moving parts and a fast release pace that you must keep up with, especially after this year’s advisories. Plain WireGuard remains the simplest answer for links that never move. For the Coimbatore pump maker, the sensible path is one of the first two for people and servers, a plain tunnel or a subnet router for the plants, a deny-by-default policy written from the list of flows, and the router’s port forwards deleted on the same day the mesh goes live.
Sources
Headscale
- juanfont/headscale on GitHub
- Headscale documentation (stable)
- Headscale: features
- Headscale: FAQ
- Headscale: client and operating system support
- Headscale: requirements
- Headscale: ACLs and policy
- Headscale: OpenID Connect
- Headscale: DERP
- Headscale: upgrade
- Headscale v0.29.6 release
- Headscale v0.29.0 release
- Headscale changelog
- Headscale LICENSE
- Headscale example configuration
NetBird
- netbirdio/netbird on GitHub
- NetBird LICENSE
- NetBird: how NetBird works
- NetBird: self-hosting quickstart
- NetBird: self-hosting guide
- NetBird: FAQ
- NetBird: manage network access
- NetBird: posture checks
- NetBird: setup keys
- NetBird: peer session expiration
- NetBird v0.81.4 release
- NetBird v0.81.0 release
WireGuard
- WireGuard home page
- WireGuard: quick start
- WireGuard: protocol and cryptography
- WireGuard: known limitations
- WireGuard: routing and network namespaces
- WireGuard: installation
- wg-quick(8) manual
- wg(8) manual
- WireGuard whitepaper (PDF)
- wireguard-go
Tailscale, for context
- Tailscale: how Tailscale works
- Tailscale: how NAT traversal works
- Tailscale: DERP servers
- Tailscale: control and data planes
- Tailscale: key expiry
- Tailscale: open source
- Tailscale: security bulletins
- tailscale/tailscale on GitHub
Other options
Security records
- NetBird security advisories
- GHSA-qcpp-8vwj-hhwr (NetBird IPC socket privilege escalation)
- GHSA-28q5-j9qm-gx35 (NetBird daemon socket file read)
- GHSA-xmg2-v32j-x2hf (NetBird SSH config injection)
- GHSA-7wwh-vfq4-v488 (NetBird SOCKS5 proxy binding)
- GHSA-v5w2-pqxj-6r94 (NetBird relay handshake)
- GHSA-fhw6-5mff-2gfh (NetBird relay WebSocket DoS)
- GHSA-jxxc-7jxf-v8hm (NetBird SSH server x/crypto/ssh)
- GHSA-rxmp-8h9v-56cx (NetBird management UpdateUser race)
- GHSA-4g2w-p87c-hxch (NetBird MSI repair privilege escalation)
- NVD: CVE-2023-47390 (Headscale tokens in logs)
- NVD: CVE-2026-46484 (Headplane)
- GHSA-vqp6-rc3h-83cp (Tailscale Windows daemon CSRF)
- GHSA-qccm-wmcq-pwr6 (Tailscale daemon CSRF information disclosure)
- GHSA-vfgq-g5x8-g595 (Tailscale SSH on FreeBSD)
- GHSA-69x3-g4r3-p962 (Nebula blocklist bypass)
Internal
Releted Posts
Uptime monitoring in 2026: revisit Uptime Kuma, Gatus and the Blackbox Exporter before a customer tells you the site is down
Take a travel company in Udaipur that runs an online booking site for about 140 heritage hotels, lake-side guest houses and homestays in Udaipur, Kumbhalgarh and Mount Abu (an invented example, not a real client).
Read moreSelf-hosted Git forges in 2026: revisit Gitea, Forgejo and GitLab CE before you move another repository
Picture a software company in Kochi that builds billing and export-documentation software for seafood exporters in Aroor, Alappuzha, Kollam and Thoothukudi (an invented example, not a real client).
Read moreContainer registries in 2026: revisit Harbor, Zot and CNCF Distribution before you push another image
Picture a product engineering team in Bengaluru that runs about forty microservices on Kubernetes for a B2B payments product used by NBFCs across Karnataka and Maharashtra (an invented example, not a real client).
Read more