All Articles
Tutorialsnetbirdwireguardvpnself-hostingnetworkinglinodedevopssmall business

Self-Hosting NetBird: A Private Mesh VPN on a Small Linode

23 July 2026· 17 min read· Andrew Arscott

This article contains referral links — if you sign up through them, I may earn a credit or commission at no extra cost to you.

Introduction

Ask most small businesses how their remote team connects back to the office network, and the honest answer is usually "badly." Not through negligence — through the fact that every conventional option for giving a handful of remote workers secure access to internal resources is either fiddly to the point of breaking on half your team's devices, or priced as if you were a 500-seat enterprise.

The shortcut a lot of small businesses reach for instead is simpler, and worse: open up RDP directly to the internet and let people log in to their desktop from home. It works, right up until it doesn't. Expose port 3389 to the world and it isn't long before it's found — not by anyone specifically targeting you, just by the constant background scan of bots that treat every public IP as a lottery ticket. What follows is a slow-building storm of login attempts, hammering away around the clock, chewing through bandwidth and CPU until the server is struggling to respond to anyone — including you, trying to get in the front door yourself. Getting locked out of your own machine by other people's brute-force traffic is a special kind of frustrating.

And that's before you get to the part where the brute-forcing actually works. Password complexity rules can mandate length, a number, a symbol — and still not save you from the one person on the team who satisfies all of it with their dog's name and their birthday. It technically passes the policy. It's also the first thing any credential-stuffing list tries.

So the "sensible" fix is to lock RDP down to a firewall allow-list of known IPs. Someone on the team pipes up with "don't worry about me, I've got a static IP from my ISP" — so you dutifully add it to the list and move on. Weeks later you get the phone call: they can't get in anymore. Turns out the IP was never static at all, just a dynamic one their ISP happened to hold onto for a long time — right up until a power cycle, a local network event, or nothing identifiable at all handed them a new one, always at the least convenient moment. Virgin Media, in my experience, was particularly good at this — dynamic IPs that sat still for months at a time, long enough for everyone to forget they weren't static, before quietly rotating. Vodafone was the opposite problem: IPs that could shift almost hourly, turning an allow-list into something you'd be updating before your coffee had gone cold.

I've been on both sides of that problem, and it's worth walking through why each of the usual "proper" routes falls apart too, before getting to the thing that actually fixed it for me.

The Windows Server VPN Trap

The dream, for a lot of small businesses, is simple: log in with the same domain credentials you use at your desk, and you have the same access from home. Windows Server's built-in VPN role makes that feel within reach — for a Windows laptop tied to the domain, it mostly delivers exactly that.

The problem is the rest of the estate. Most small teams today aren't a room full of identical company-issued Windows laptops — they're a mix of BYO devices, different shapes, different operating systems. The moment a MacBook, an iPhone, or a personal Android phone needs access, the neat "just log in with your normal credentials" story quietly stops being true. It's a setup that's shown its age, and it was never really built with a varied, bring-your-own-device estate in mind.

OpenVPN: Solves Compatibility, Creates a Different Problem

OpenVPN is usually the next stop, and it earns its reputation — it genuinely works everywhere, on everything. But rolling it out to a small, non-technical team trades one problem for another. Instead of a domain login, each person needs a key pair installed on their device, and whoever runs the server needs to keep an eye out for network collisions as those clients are brought online. That's a manageable task for a dedicated network engineer. It's a much less comfortable one for a small company with no budget for regular remote support, where the person talking someone through installing a key file over the phone is usually the same person who set up the server in the first place.

And it only takes one deleted key. A device gets wiped, a file goes missing, someone updates their laptop — and reissuing access becomes a bigger interruption than it should ever need to be for a company this size.

And the Commercial Options Don't Fit a Startup Budget

The enterprise answer — Cisco AnyConnect, Fortinet, Perimeter 81, NordLayer and similar Zero Trust Network Access products — genuinely does solve the compatibility and management problems above. They ship polished native clients for every platform and centralised dashboards for access control. What they don't solve is cost: these are priced per user per month, often with minimum seat counts or annual contracts that make sense for a business provisioning fifty or five hundred logins, not five. For a startup watching every line of monthly spend, paying an enterprise per-seat VPN fee for a handful of remote workers is a hard sell next to almost anything else that money could do.

Enter NetBird

This is the gap NetBird fills. It's an open-source mesh VPN built on WireGuard, and because every platform runs the same protocol through the same native client, it gets closer to that original Windows Server dream than Windows Server ever managed — one login, the same experience, whether it's a company Windows laptop or someone's personal iPhone. Windows, macOS, Linux, iOS and Android all get first-class, actively maintained clients that behave identically. Every device gets its own private IP inside the mesh and connects directly to any other device, encrypted, without port forwarding, without a key file anyone can accidentally delete, and without a per-seat licence fee.

The closest comparison is Tailscale, which solves the same compatibility problem with the same underlying protocol. The difference that matters for a cost-conscious business is where the coordination server lives: Tailscale's control plane is closed-source and runs on Tailscale's infrastructure, with pricing that scales per user. NetBird's management server, signal server and relay are all open source (BSD-3) and can run entirely on infrastructure you already control — including a Linode instance costing a few pounds a month, covering as many devices as you need.

Common Use Cases

Small team remote access. The core case: a handful of people who need to reach internal tools — a shared drive, an internal admin panel, a database — without a corporate VPN appliance or per-seat licensing. Add each person as a peer, apply an access policy, done.

Remote access to a home lab or office server, without DDNS, port forwarding, or trusting whatever UPnP has quietly opened on your behalf.

Connecting cloud and on-prem resources. A database on a VPS and an internal service on an office machine can talk to each other over the mesh as if they were on the same LAN.

Secure SSH without exposing port 22. Servers can run with no public SSH port at all; you connect over the NetBird IP instead, and the attack surface for brute-force bots disappears entirely.

Multi-site businesses without dedicated VPN hardware. A couple of small offices, previously needing routers capable of site-to-site tunnels, can instead just run the NetBird client on a box in each location.

Edge Cases Worth Knowing About

Temporary contractor access. Rather than issuing VPN credentials to a whole network, add a contractor as a peer, apply a policy restricting them to exactly what they need, and remove them cleanly when the engagement ends — no certificate revocation ceremony required.

IoT and headless devices. Devices that can't run a full VPN client, or that you don't want exposed to the internet at all, can sit on the mesh with tightly scoped access rules rather than a blanket "trusted" status.

CI runners reaching internal infrastructure. A self-hosted CI runner that needs to reach an internal database or deployment target, without punching a permanent hole in a firewall for a CI provider's IP ranges.

Kubernetes cluster access. Reaching a cluster's internal services directly for debugging, without a bastion host or exposing the API server more broadly than necessary.

Self-Hosted vs. NetBird Cloud

NetBird offers a hosted version, and for a lot of people that's the right call — it removes the maintenance burden entirely. I chose to self-host because a small VPS is cheap enough that the cost argument barely exists, and because it means no reliance on a third party's uptime for something as fundamental as "can my team reach the office." If you'd rather skip everything below, NetBird Cloud gets you running in a few minutes with no server management at all.

Setting It Up on a Small Linode

NetBird's own quickstart script handles almost all of this, but it's worth understanding what it's doing rather than just running it blindly.

Requirements

Before starting, you'll need:

  • A Linux VM with at least 1 CPU and 2GB of memory — a Linode Nanode or Shared 2GB instance is comfortably enough for a small team.
  • The VM publicly reachable on TCP ports 80 and 443, and UDP port 3478.
  • A public domain name with an A record pointing at the VM's IP (e.g. netbird.example.com).

Provisioning the Linode

Create a small Ubuntu instance through the Linode dashboard — a Nanode 1GB will technically run it, but I'd recommend the 2GB Shared plan for headroom, especially if you'll add a proxy service or CrowdSec later. Ubuntu 22.04 or 24.04 both work fine.

If you're new to Linode, signing up through this referral link gets you a $100, 60-day credit once you've added a valid payment method — more than enough to run this whole setup for free while you try it out.

Once it's up, point a DNS A record at its public IP:

netbird.example.com    A    YOUR.LINODE.IP.ADDRESS

If you plan to enable NetBird's proxy service (for selectively exposing internal resources), add a wildcard CNAME too:

*.netbird.example.com    CNAME    netbird.example.com

Installing Prerequisites

SSH into the box and install Docker along with jq and curl:

sudo apt update
curl -fsSL https://get.docker.com | sh
sudo apt install jq curl -y

Running the Installer

NetBird ships a script that generates a complete, production-ready deployment:

curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bash

It will ask which reverse proxy to use. Unless you already run one, choose the default — Traefik — which handles TLS certificates automatically via Let's Encrypt with no extra configuration. It will then ask whether to enable the NetBird Proxy service, which lets you selectively expose internal resources to the internet later; you can safely say no for now and enable it afterwards if you need it.

The script generates:

  • docker-compose.yml — the full set of services (management, signal, relay, dashboard, and Traefik)
  • config.yaml — the combined server configuration
  • dashboard.env — environment variables for the dashboard container

Once it finishes, the stack is already running under docker compose.

Firewall

If you're managing your own firewall on the box (rather than relying solely on Linode's Cloud Firewall), open the required ports:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 3478/udp
sudo ufw enable

If you're using Linode's Cloud Firewall instead, mirror the same rules there — Linode's firewall sits in front of the instance, so both need to agree.

First Login

Navigate to https://netbird.example.com in a browser. Because no users exist yet, you'll be redirected to /setup, where you create the first admin account with an email, name and password. From that point, /setup stops being reachable — the URL only works before any account exists — and you log in normally.

From the dashboard you can add more users, either through NetBird's built-in local user management or by wiring up an external identity provider like Google, Microsoft Entra ID, or Okta for SSO.

Adding Peers

With the server running, install the NetBird client on any device you want on the mesh — Windows, macOS, Linux, iOS and Android are all supported natively, which is the whole point — point it at your self-hosted management URL, and authenticate. It'll appear in the dashboard as a peer, and from there you can group devices and write access policies rather than leaving every peer able to see every other peer by default.

Locking Down User Access with Zitadel

NetBird's built-in local user management is fine for a single admin account, but the moment there's an actual team, it's worth putting a proper identity provider in front of it rather than relying on a flat list of accounts. Zitadel is the natural fit here — it's open source, it's designed for exactly this kind of self-hosted deployment, and NetBird ships a dedicated installer for it that deploys alongside the rest of the stack on the same domain, rather than needing a subdomain of its own:

curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started-with-zitadel.sh | bash

Once it finishes, the script creates a default admin account for you — something like admin@netbird.example.com — with a randomly generated password written out to the deployment's .env file. Grab that before you close the terminal.

The Zitadel admin console is reachable at:

https://netbird.example.com/ui/console

Log in with the generated admin credentials and you're in a proper identity platform — creating users, assigning roles, organising your team — rather than a bare list of logins. This is also where you'd invite colleagues one at a time, deliberately, rather than leaving the door open.

SMTP, so invites and password resets actually arrive. Zitadel won't send a single email — invite links, password resets, none of it — until an SMTP provider is configured, and this isn't something the installer sets up for you. It's also not something you can bolt on later through an environment variable; Zitadel only reads SMTP settings from its startup configuration once, on a fresh instance's first boot, so editing .env after the fact and restarting the container won't do anything. Configure it through the console instead, under Instance → Settings → Notifications → SMTP Provider, pointing it at whatever you use for transactional email — Postmark, SES, or a personal relay all work.

Turning off self-registration. Left on its default, Zitadel's login page happily lets anyone create an account — which is exactly backwards for something guarding access to your business network. Under Instance → Settings → Login Behavior and Security, disable Allow Registration in the login policy. From that point, the only way an account gets created is an admin adding one deliberately through the console — which, for a handful of remote workers, is exactly the level of ceremony you want.

Ongoing Maintenance

A self-hosted service is still a service, and the usual rules apply — something I've written about before in a different context. NetBird's docs cover backups and upgrades directly; both are worth setting a recurring reminder for rather than discovering you need them mid-incident.

Closing Thoughts

None of the pain in the traditional options — RRAS protocol roulette, OpenVPN's certificate housekeeping, enterprise per-seat pricing — is a reason remote access should be hard for a small team. It's a reflection of tooling that was built for a different scale of business. A small Linode instance, a domain, and twenty minutes with NetBird's installer gets a startup the same cross-platform reliability as the enterprise products, without the enterprise invoice.

Have thoughts on this? Get in touch.