Why Self-Hosted Mesh VPN Is Worth the Setup Time
Traditional VPNs route all your traffic through a central server, which creates a bottleneck and a single point of failure. Mesh VPNs work differently – each device connects directly to others using encrypted peer-to-peer tunnels, so if one node goes down, the rest of the network stays intact. Netbird takes this architecture and wraps it in a surprisingly clean management interface, making it possible to link servers, home machines, and cloud instances into one private network without exposing anything to the public internet.
What sets Netbird apart from similar tools is its use of WireGuard under the hood, combined with a control plane you can run entirely on your own infrastructure. The cloud-hosted version works fine for experimentation, but self-hosting the management platform means your network topology, peer lists, and access rules never leave your own servers. This guide walks through deploying that self-hosted setup from scratch.

What You Need Before Starting
The self-hosted Netbird stack requires a Linux server with a public IP address – a small VPS works perfectly. You will need Docker and Docker Compose installed, a domain name you control, and ports 80, 443, 3478, 49152 through 65535 (UDP), and 10000 open in your firewall. The domain is non-negotiable because Netbird’s management API and dashboard are served over HTTPS, and the TURN server used for NAT traversal expects a valid hostname. A single-core VPS with 1 GB of RAM handles dozens of peers without strain.
Before pulling any configuration files, point your domain’s DNS to the server’s public IP. Use a subdomain like netbird.yourdomain.com and let it propagate. Netbird’s setup script provisions TLS certificates automatically via Let’s Encrypt, but it needs DNS resolving correctly first or the cert request will fail and stall the entire installation. Check propagation with a tool like dig netbird.yourdomain.com before moving forward.
Installing the Netbird Management Stack
Netbird provides an automated setup script that generates a Docker Compose configuration along with all required environment files. Pull it down and run it in a dedicated directory:
- Create a directory: mkdir -p /opt/netbird && cd /opt/netbird
- Download the setup script: curl -sSL https://github.com/netbirdio/netbird/releases/latest/download/netbird_install.sh -o install.sh
- Make it executable: chmod +x install.sh
- Run with your domain: sudo ./install.sh –domain netbird.yourdomain.com
The script will prompt you to choose an identity provider. For a fully self-hosted setup with no external dependencies, select the built-in Zitadel option. Zitadel is an open-source identity platform that Netbird bundles into its compose stack specifically for this use case. It handles user authentication, token issuance, and SSO without requiring a Google, Azure, or GitHub account as a backend. The script writes a .env file with generated secrets – back this file up immediately because losing it means rebuilding the entire auth stack from scratch.
Once the script finishes, start the stack with docker compose up -d. The first boot takes two to three minutes because Zitadel runs database migrations before accepting connections. Watch the logs with docker compose logs -f zitadel and wait for a line confirming the system is ready. After that, the Netbird dashboard should be accessible at https://netbird.yourdomain.com.
The initial login uses credentials printed at the end of the setup script output – typically an admin email and a generated password. Log into Zitadel first at https://netbird.yourdomain.com/ui/console, change the admin password, then return to the Netbird dashboard. This two-step initialization is the part most people skip, which causes authentication errors later when they try to enroll the first peer. Creating a regular user account in Zitadel for day-to-day use is also worth doing now rather than running everything as the system admin.

Enrolling Your First Peers
With the management plane running, enrolling devices is straightforward. Install the Netbird client on any machine you want to add to the network. On Debian or Ubuntu systems, the official repository handles this cleanly:
- Add the repo: curl -fsSL https://pkgs.netbird.io/install.sh | sudo bash
- Log in and point the client at your server: netbird up –management-url https://netbird.yourdomain.com
- A browser window opens asking you to authenticate through Zitadel
- After auth completes, the peer appears in your dashboard under Peers
Repeat this on every machine you want to include – home server, cloud VPS, laptop, or Raspberry Pi. Each device receives a private IP in the 100.64.x.x range automatically. By default, all peers in the same account can reach each other, but Netbird’s access control rules let you restrict this to specific groups if you are managing a larger setup with multiple users or environments.
Configuring Access Control and Routes
The dashboard’s Access Control tab is where the real network design happens. Netbird uses a policy model where you define which peer groups can communicate with which other groups. Out of the box, a default policy allows all peers to reach each other, which is fine for personal use but worth tightening if you are onboarding other users. Create groups – call them something like servers, laptops, and trusted-users – then write policies that explicitly state which combinations are allowed to talk.
Network routes extend the mesh beyond enrolled peers. If you have a home network on 192.168.1.0/24, you can designate one enrolled machine on that network as a routing peer. Under the Network Routes section, add that subnet and assign the routing peer. Every other device in your Netbird network will then be able to reach anything on your home LAN without installing Netbird on each individual device there. This is the feature that makes Netbird genuinely useful for home lab access rather than just machine-to-machine tunneling.
DNS configuration deserves attention too. Netbird can push custom DNS settings to peers, so you can resolve internal hostnames across the mesh without running a separate DNS server. Under DNS in the dashboard, add nameservers or domain-specific resolvers. If your self-hosted services run on a local domain like .internal, pointing peers at your internal resolver here means you can reach vaultwarden.internal from any enrolled device – a natural fit if you are already running services like Vaultwarden behind a reverse proxy on your home server.

Keeping the Stack Running Long-Term
Netbird’s compose stack does not update itself. When new versions ship – which happens frequently given the project’s pace – you need to re-run the setup script or pull updated images manually with docker compose pull && docker compose up -d. Set a reminder to check the GitHub releases page monthly. The project maintains a changelog that flags breaking changes clearly, and most version jumps are painless. The Zitadel container occasionally requires manual database migration steps on major version bumps, so reading the release notes before pulling is not optional.
Backups should cover three things: the /opt/netbird/.env file, the Docker volumes holding the Netbird management database, and the Zitadel database. The quickest approach is a cron job that dumps both databases nightly and ships the output offsite. If the server dies and you only have the .env file, you can restore the stack but you will lose all peer configurations and access rules. Losing the .env file means starting over entirely, because the secrets in it are what tie Zitadel’s tokens to Netbird’s management API.
Peers that go offline do not automatically clean themselves out of the dashboard. Over time, decommissioned machines accumulate in the peer list and clutter the interface. Netbird does not yet have an auto-expiry setting, so periodic manual cleanup is part of the maintenance routine – a small but persistent friction point in an otherwise low-overhead setup.





