How to self-host NetBird VPN server for VPS
Set up NetBird on one VPS with DNS, TLS, the pinned quickstart script, setup keys for unattended peers, plus a clear comparison with Headscale.
Wetín self-hosting NetBird VPN server dey give you
Self-hosting NetBird VPN server dey put the control plane for VPS wey you own: na the part wey dey keep peer list, decide which machine fit reach which one, and help two peers find each other behind NAT (network address translation). The tunnels themselves still na WireGuard, and dem dey encrypted directly between your machines. Wetin change be say no outside company dey hold your device inventory or your login flow. Understand clearly wetin this arrangement dey give you, because hosted control plane no dey ever hold the keys wey encrypt your traffic too, and wetin coordination server fit really do if somebody breach am na shorter list than most people dey assume before dem read am.
NetBird dey sit between two things wey you fit already know. E be mesh overlay, so peers dey connect to each other instead of sending everything through one gateway. E also fit self-host from end to end, and this put am against Headscale, the self-hosted Tailscale control server. If na only single-gateway tunnel you don ever run, read the difference between plain WireGuard and mesh overlay first, because na that mental model go make the rest of this page useful.
If wetin you really want na one server wey all your traffic go exit from, mesh na more machinery than the work need. Plain WireGuard VPN for one VPS or Tailscale exit node fit do am with much less thing to run. And if the goal na to reach one private network instead of linking machines to each other, Tailscale subnet router for VPS dey advertise that range to tailnet wey you already get, without any part of the stack below.
Wetin the stack actually dey run
The layout change recently, and most older write-ups still describe the old one. As of August 2026, for release v0.76.2, the quickstart script dey write Compose file with three services by default.
netbird-serverdey carry the management API, the signal service, the relay with embedded STUN listener, and embedded identity provider. For older releases, dem be separate containers, and the identity provider na separate Zitadel install wey you need build first.dashboardna the admin web console.traefikdey terminate TLS (transport layer security) and request certificate from Let's Encrypt the first time e start.
Two more services dey available, but dem stay off unless you answer yes for prompt. NetBird Proxy service dey publish internal services on public hostnames. CrowdSec dey filter abusive traffic. You no need any of dem to build working mesh, and both fit chop memory for small box.
If you dey come from wg-easy for one Docker container, this one get plenty more parts. Wetin e give you na access policies and per-user accounts, plus peers wey connect directly to each other instead of passing through one gateway.
Wetín you need before you start
A public domain name no be optional. The dashboard, the API and the relay all dey use HTTPS for port 443, and Traefik dey collect its certificate from Let's Encrypt with HTTP challenge. This needs a name wey resolve to this VPS from public internet. Bare IP address no go work for this setup.
Create one A record, netbird.example.com pointing to the VPS public IPv4 address, and wait for DNS to propagate before you run anything.
dig +short netbird.example.comThat command must print your server address. If you run the installer before DNS propagate, the certificate request go fail for first start. Repeated failed validations fit reach Let's Encrypt rate limits, so you go need wait one hour before you try again.
Three ports must dey reachable from internet: TCP 80 for certificate challenge and redirect to HTTPS, TCP 443 for dashboard, API, signal and relay traffic, and UDP 3478 for STUN.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 3478/udp
sudo ufw reload
sudo ufw statusOpen dem for your provider network firewall too. For most VPS panels, this na separate control. Na this one dey make a box whose own ufw status looks correct still refuse connections.
STUN (session traversal utilities for NAT) na how one peer dey learn the public address and port wey its own NAT assign, so two peers fit try direct tunnel. If you block UDP 3478, peers still go connect through the relay on TCP 443, so nothing go look broken. Instead, you go get Connection type: Relayed for every peer, and all traffic go pass through your VPS instead of going peer to peer.
For the software side, you need Docker with the Compose v2 plugin, plus jq and curl. The script checks all of dem and stops if any one dey missing. If Docker new for this box, first get Docker Compose working for the VPS.
Ports if you skip the bundled reverse proxy
If you run without Traefik, the individual services go expose directly, and the port list go increase:
- TCP 80, HTTP redirects
- TCP 443, HTTPS
- TCP 33073, management gRPC
- TCP 10000, signal gRPC
- TCP 33080, relay over WebSocket or QUIC
- UDP 3478, STUN
Choose this only when the box already dey terminate TLS for another service. Otherwise, the bundled Traefik means fewer rules and fewer mistakes.
Install NetBird server with the quickstart script
The documented one-liner dey pipe the newest release straight go shell:
curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bashPin am instead. latest dey change, so the same command wey you run two weeks apart go produce two different installs, and nothing for disk go record which one write your config. Download a tagged release, read am, then run am.
mkdir -p ~/netbird
cd ~/netbird
curl -fsSL -o getting-started.sh \
https://github.com/netbirdio/netbird/releases/download/v0.76.2/getting-started.sh
less getting-started.sh
bash getting-started.shThe script go ask for the domain first:
Enter the domain you want to use for NetBird (e.g. netbird.my-domain.com):Then e go ask how TLS go work:
Which reverse proxy will you use?
[0] Traefik (recommended - automatic TLS, included in Docker Compose)
[1] Existing Traefik (labels for external Traefik instance)
[2] Nginx (generates config template)
[3] Nginx Proxy Manager (generates config + instructions)
[4] External Caddy (generates Caddyfile snippet)
[5] Other/Manual (displays setup documentation)
Enter choice [0-5] (default: 0):Choose [0]. Options 2 through 5 dey write a config snippet and leave the wiring for you. This one correct for box wey already dey run proxy, but e wrong for fresh one. Option 0 go then ask for a Let's Encrypt email address. Dem go use am for expiry notices.
For first install, answer no to NetBird Proxy service. E need two extra DNS records, proxy.netbird.example.com and wildcard *.proxy.netbird.example.com, and e no do anything for plain mesh. Answer no to CrowdSec too. You fit add both later.
The script dey write these files for current directory: docker-compose.yml, config.yaml with mode 600, dashboard.env, and traefik-dynamic.yaml if you choose bundled Traefik. Treat that directory as state wey you must keep, because config.yaml hold the key wey dey encrypt data for the store. If you lose am, reinstall no go fix the problem.
docker compose ps
docker compose logs -f netbird-serverEvery service suppose read running, and server log suppose settle instead of restarting in a loop. Monitor the certificate separately:
docker compose logs traefik | grep -i acmeACME (automatic certificate management environment) na the protocol wey Traefik dey use to get the certificate. Errors for here almost always mean DNS problem or say port 80 dey closed.
First admin account create
Open https://netbird.example.com. If na fresh install, e go open setup page instead of login form. Enter email address, name, and password, then click Create Account. This one go become the first admin, and the page go redirect you to login form.
That account dey inside NetBird own user store. An identity provider wey dey inside netbird-server container dey power am. Nothing external dey involved. This na the biggest change from self-hosted NetBird of one year ago. That time, working install mean say you first need set up Zitadel or Keycloak, then copy four OIDC (OpenID Connect) values into setup.env before anything fit start at all.
If browser show certificate warning instead of setup page, certificate no issue. Fix am before you continue. Dashboard dey talk to API through the same hostname, and bad certificate fit make e fail for confusing ways.
Join your first peer
Install the client for any Linux machine, including the VPS itself if you want make e join the mesh:
curl -fsSL https://pkgs.netbird.io/install.sh | shFor Debian and Ubuntu, that script dey configure NetBird package repository, then e install the client through apt. So na the package manager dey manage am either way. If you no like pipe script enter shell, save am first with curl -fsSL -o install.sh https://pkgs.netbird.io/install.sh and read am before you run sh install.sh. Either way, confirm wetin install:
apt-cache policy netbirdnetbird na the command line client and the daemon. netbird-ui na the desktop tray app, and headless server no get use for am.
Now point the client to your server:
sudo netbird up --management-url https://netbird.example.comIf you omit --management-url, the client go register with NetBird hosted service, because na the compiled-in default. The command still go succeed, the machine still go get address, and your self-hosted dashboard go remain empty. This one catch almost everybody at least once.
The command go print URL wey you go open for browser to finish the login. After that:
netbird status
ip addr show wt0Read four lines from netbird status: Management: Connected, Signal: Connected, one Relays: line wey dey report every available relay, and one NetBird IP: for the overlay range. wt0 na the WireGuard interface wey NetBird create, and e suppose carry that same address.
Join second machine without person to set am
Browser login no work for machine wey no get browser and nobody dey in front of am. Setup key na pre-authentication token wey register machine without the interactive step. Create one for dashboard under Setup Keys.
Two kinds dey. One-off key authenticate exactly one machine, then dem don use am finish. Reusable key fit register many machines, with optional limit for the number. Both get expiry time, and both fit automatically assign the new peer to group, so the access rules for that group apply immediately the machine show.
sudo netbird up --setup-key <SETUP-KEY> \
--management-url https://netbird.example.com \
--hostname build-runner-01--hostname set the name wey dashboard go show. If you no set am, peer go use the name wey machine call itself, and fleet of entries wey all get name ubuntu no help anybody.
For containers and build agents wey no dey live long, mark the key as ephemeral when you create am. Peers wey ephemeral key register go remove automatically after dem don stay offline pass 10 minutes. This one keep dead entries comot from peer list.
Understand this limit before you plan around setup keys: when key expire or you delete am, e stop new registrations, but e no disconnect machines wey don already register with am. To remove machine access, you need remove that peer.
You still need separate identity provider?
For small installation, no. The built-in user store dey handle accounts wey people create from dashboard, and that one dey enough for small number of users.
You need external identity provider when you already get one and you no want maintain second list of users. NetBird dey accept any provider wey support OIDC. Register confidential OIDC client for your provider, then add am for NetBird dashboard with four values: name, client ID, client secret and issuer. NetBird go give you redirect URL wey you go paste back for the provider. Named integrations dey available for Google, Microsoft Entra ID, Okta, Zitadel, Keycloak, Authentik and Pocket ID. Anything else go enter as generic OIDC. If you already dey run Authentik as your self-hosted single sign-on, na this path go keep one account list instead of two.
Local login still dey available after you add provider, and every provider wey you configure go show for login page. Keep one local admin account with strong password. If OIDC configuration break, you still get way to enter.
NetBird or Headscale: which control plane you suppose run?
Both remove the same dependency: the hosted control server wey your clients for otherwise call home to. But the two projects no get the same shape.
Headscale dey reimplement Tailscale control server, and you still use the official Tailscale clients. Official web console no dey. You dey manage users and pre-authentication keys with the headscale command against a config file. Community web interfaces dey, but dem no be part of the project. This one suit people wey want make their state dey inside files and their changes dey version control.
NetBird ships the complete product: e own client, e own dashboard, embedded identity provider, and access policies wey you edit for browser. This one mean say more moving parts go dey your VPS, but e reduce the work well well when you wan hand am to colleague wey no go ever open terminal.
Run Headscale if you don already invest for Tailscale clients or you want the smallest control plane wey possible. Run NetBird if different people need manage peers and you want console and SSO without assembling one yourself. Before you commit to either one, check wetin Tailscale free plan really cover, because group wey fit stay within six users with unlimited devices no go pay anything for hosted control plane and fit no get reason to run one at all. Once you pass that limit, the bill dey grow based on number of people, not number of machines. So work out wetin Tailscale go charge your group to get figure wey you fit compare with the VPS cost and the hours wey this stack go take from you.
How small VPS fit run this?
The documented minimum na 1 CPU and 2 GB memory. NetBird own notes put the current floor near 1 GB RAM now wey user management don become local, compared with the 2 GB to 4 GB wey the older layout need when full Zitadel deployment dey part of the stack. Buy 2 GB. The extra headroom na wetin allow upgrade pull new images while the old ones still dey disk.
Three things safe to leave out for small box. Decline the NetBird Proxy service. E dey publish internal services for public hostnames, and e no get anything to do with peers wey dey connect. Decline CrowdSec too. E worth adding later for box wey expose to internet, no be on day one. Keep the default SQLite store for the netbird_data volume. Move to PostgreSQL only when you split the deployment across machines or encounter real concurrency. Documentation describe this as migration wey you fit do later.
The relay na the one component wey you no fit remove. Two peers wey their NAT dey assign different port for every destination no go ever establish direct tunnel. So relay na the only path wey make dem work at all. Disabling am save very little memory, but e break connections in a way wey hard to trace.
When one box no longer enough, relays na the first thing to move away from am. Standalone relay dey run with NB_LISTEN_ADDRESS, NB_EXPOSED_ADDRESS, NB_AUTH_SECRET and NB_ENABLE_STUN. The shared secret must be identical for the relay and the main server. If not, clients no go authenticate to am.
Failure modes, and wetin you go see
Dashboard dey show certificate warning. Traefik no obtain certificate. Run docker compose logs traefik | grep -i acme. E get two possible causes. Either dig +short netbird.example.com never dey point to this VPS yet, or TCP 80 dey closed somewhere between Let's Encrypt and the container, usually for provider network firewall instead of ufw. Fix the cause before you retry repeatedly, because failed validations get rate limit and you go lock yourself out from retries for one hour.
Client talk say e connect but dashboard empty. Client register with NetBird hosted service because --management-url dey missing. Run netbird status --detail and read the Management: line; e go show the server wey client dey actually talk to. If you see Management: Connected to https://api.netbird.io:443, e mean say client go cloud. Run sudo netbird down, then run sudo netbird up --management-url https://netbird.example.com again.
Every peer dey show Connection type: Relayed. No direct tunnels dey form, so all traffic dey pass through your VPS and add one latency hop. Check UDP 3478 for VPS firewall and provider firewall, because STUN na wetin allow peer learn its own public address and port. netbird status --detail also dey print Direct: false and the ICE (interactive connectivity establishment) candidate types for each peer. This one show how far the attempt reach. For some networks, relayed na the only outcome wey dey available, and nothing dey wrong.
Peer join but e no fit reach anything. To dey inside mesh no mean say two peers fit talk to each other. Access policies decide this, and group wey no get policy attached no fit reach anything. Check the policy for dashboard before you start debugging routes and firewalls.
netbird status report daemon problem. The service no dey run. Use sudo netbird service status and sudo netbird service start. Client logs dey for /var/log/netbird/client.log. For anything wey you no fit identify, netbird debug bundle --anonymize --system-info go collect logs, status, routes, DNS settings and firewall state into one archive.
Backups and upgrades
Two things dey carry the whole install: the directory wey hold docker-compose.yml and config.yaml, plus the Docker volume wey hold the database and encryption keys. Back dem up together. config.yaml hold the key wey dey encrypt data for the store, so database copy without am go restore to nothing wey you fit read.
docker volume ls
docker compose down
sudo tar czf netbird-config.tgz -C ~ netbird
docker run --rm -v netbird_netbird_data:/data -v "$PWD":/backup \
alpine tar czf /backup/netbird-data.tgz -C /data .
docker compose up -dCompose dey prefix volume names with the project directory, so the volume wey dem document as netbird_data normally dey appear as netbird_netbird_data. Run docker volume ls first and use the name wey e print, or docker run above go silently create empty volume and archive nothing. Keep the archives away from the VPS. If you already get backup tool, restic or BorgBackup fit handle the offsite part.
Upgrading the server na to pull and recreate:
docker compose pull
docker compose up -d
docker compose psBefore you depend on am, run docker compose config | grep image:. Any tag wey read latest suppose get pinned to a version, for the same reason wey you pin the install script: you need know wetin dey run, and you need get version wey you fit return to when upgrade no behave well. Clients dey upgrade through whichever package manager install dem.
FAQ
I need my own identity provider to self-host NetBird?
No. Current releases get built-in user store, so you fit create the first admin account for browser at https://netbird.example.com, then add users from the dashboard afterwards. External OIDC provider na optional, and you fit add am later with four values: name, client ID, client secret and issuer. Guides wey tell you make you deploy Zitadel or Keycloak before NetBird dey describe setup wey no longer necessary, and if you follow dem, you go need run one extra service.
Why all my peers dey show Connection type: Relayed?
Direct connections no dey form, so traffic dey pass through relay for your VPS. The common cause na UDP 3478 wey block, and na the STUN port peers dey use to discover their own public address and port. Open am for VPS firewall and for your provider separate network firewall, then run netbird status --detail again and read the Direct: line. For network wey NAT dey assign different port for each destination, relayed na the only possible result, and nothing misconfigured.
My client connect, but dashboard no show any peers. Wetin happen?
The client register with NetBird hosted service instead of your server. This one dey happen when --management-url no dey included. netbird status --detail dey print the server wey e dey talk to for the Management: line, so value like https://api.netbird.io:443 confirm am. Run sudo netbird down, then sudo netbird up --management-url https://netbird.example.com, and the peer go appear for your dashboard.
How self-hosted NetBird different from Headscale?
Both replace hosted control server with one wey you run yourself. Headscale na control plane only: you manage am with headscale command and config file, no official web console dey, and e dey drive official Tailscale clients. NetBird ships its own client, admin dashboard and identity provider integration for the same stack. Headscale smaller to run and e keep its state for files. NetBird easier to give people wey no go use terminal.
Which VPS size self-hosted NetBird server need?
The documented minimum na 1 CPU and 2 GB of memory, and 2 GB na the amount you suppose buy. The practical floor don drop reach around 1 GB for recent releases because identity provider dey embedded now instead of being separate deployment. Decline optional proxy and CrowdSec services during install, and remain with default SQLite store until you truly need PostgreSQL.