If you're trying to self-host a Netbird deployment, you probably already know why you want to do that. If you're not sure, it allows you to form a virtual private network of your devices over the internet: i.e., easily and securely access your own devices as long as they're connected to the internet. I used Hetzner to host the Netbird deployment, but you can use your own hardware or any other cloud provider to do the same.
This is mostly a reproduction of Netbird's self-hosted quickstart guide. By the time this is done, you'll have
This is completely optional, but it can be a nice way to segregate this fundamental piece of infrastructure from whatever else you're doing.
Whether or not you have this in an isolated project, you can create a named firewall configuration just for the Netbird server. The reason to do this before creating the VPS is that it's one click to add it to your new VPS after.
The required ports for the deployment is listed here: https://docs.netbird.io/selfhosted/selfhosted-quickstart#requirements
Protocol | Port | Reason |
---|---|---|
TCP | 80 | HTTP (Let's Encrypt) |
TCP | 443 | HTTPS (Dashboard & API) |
TCP | 33073 | NetBird Management |
TCP | 10000 | NetBird Signal |
TCP | 33080 | Zitadel |
UDP | 3478 | STUN |
UDP | 49152-65535 | TURN relay |
Even the leanest VPS is enough to run the control server, so we'll only be considering the cheapest configurations. Choose a region close to your primary location. Netbird will use this VPS for
In the region closest to me (us-west
), Hetzner only offers x86 instances.
These are a bit more expensive than Hetzner's Arm instances, but they're 100ms closer than the closest Arm instances in Helsinki.
I created the VPS with Debian 12 because
curl
and jq
are easy to installHetzner attaches a public IPv4 address to each server by default (for an additional fee). Note this IP address for the DNS configuration.
I attached the firewall rules defined above when I created the VPS.
You can either provide an SSH key, or omit this and have Hetzner email you a password. If you opt for a password, you will have to set a new one the first time you ssh in. If you choose a password, you can always add an SSH key later.
Netbird's installation script expects to be able to provision a TLS certificate, so it will need a valid domain name aimed at itself before you run it.
I added an "A" record to my DNS.
I will use netbird.example.com
as a placeholder for this post.
| Type | Host | Answer |
|-|-|-|-|
| A | netbird.example.com |
https://docs.netbird.io/selfhosted/selfhosted-quickstart#requirements
Connect to your VPS via ssh
ssh root@netbird.example.com
update its software, and install curl
and jq
:
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y curl jq
Then install Docker. Check the website for up-to-date instructions
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Then, do the post-installation step
sudo usermod -aG docker $USER
Exit and ssh in again
I used the one-shot "easy install" script Netbird provides:
export NETBIRD_DOMAIN=netbird.example.com; curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started-with-zitadel.sh | bash
It will take a minute or two to fire up and configures several Docker containers running the various components of the deployment.
netbirdio/relay:latest
netbirdio/dashboard:latest
netbirdio/signal:latest
coturn/coturn
netbirdio/management:latest
ghcr.io/zitadel/zitadel:v2.64.1
postgres:16-alpine
caddy
If all goes well you will see a message like this in the terminal:
You can access the NetBird dashboard at https://netbird.example.com
Login with the following credentials:
Username: admin@netbird.example.com
Password: <some super long password here>
Open up your favorite browser and navigate to https://netbird.example.com
Put in the Username and Password provided above.
You will immediately be asked to set a new password.
Now we'll add another use to authenticate interactive client devices (e.g. phone, laptop) to the network.
Navigate to https://netbird.example.com/ui/console/users
.
I can't figure out how to get there by clicking around from https://netbird.example.com
, so I just navigate to that URL directly.
The first time the user logs in, they will have to choose a new password of their own.
A setup key can be provided to the Netbird agent to allow agents to be added non-interactively to your network. These must be kept secret, as anyone who has them and the address of your control server will be able to connect to your network.
Navigate to https://netbird.example.com/setup-keys
.
When you create a key you will be asked the following
The devices added under each key can be viewed separately in the Netbird UI, so I create one key for Docker containers (see below) and one setup key for actual hardware servers. They both are reusable with no limits and no expiration.
This is a convenient way to connect a specific Docker container to the network.
Here is an example lifted from this netbirdio Github issue using nginx
as an example.
In this example, the traffic for Nginx all goes through Netbird. You can also optionally make the container available without Netbird by exposing ports. The Netbird agent needs a persistent volume or filesystem mount to store some of its state if you want it to keep the same domain name and/or IP address.
version: '3.8'
services:
nginx:
image: nginx:alpine # just an example of a container you want to access via your Netbird network
volumes:
- ./content:/usr/share/nginx/html:ro
# ports:
# - 80:80 # needed for access without netbird
# network_mode: "service:netbird" # or you can put the equivalent on the other service
netbird:
image: netbirdio/netbird:latest
volumes:
- ./client:/etc/netbird
cap_add:
- NET_ADMIN
- SYS_ADMIN
- SYS_RESOURCE
environment:
NB_SETUP_KEY: <SETUP_KEY>
NB_HOSTNAME: <hostname>
NB_MANAGEMENT_URL: https://netbird.example.com # change to your domain
# link NetBird network to Nginx
network_mode: "service:nginx" # or you can put the equivalent on the other service
You can use the ports
field on your server image to also make it available without Netbird.
Your sidecar Netbird container should mount a volume into /etc/netbird
.
This allows netbird to save some state if the container restarts, preserving the IP address and domain name.
You will need to provide a setup key <SETUP_KEY>
to connect and a hostname <hostname>
to control how your service will appear on your network (as <hostname>.netbird.selfhosted
).
Check Netbird docs for up-to-date instructions:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg -y
curl -sSL https://pkgs.netbird.io/debian/public.key | sudo gpg --dearmor --output /usr/share/keyrings/netbird-archive-keyring.gpg
echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | sudo tee /etc/apt/sources.list.d/netbird.list
Then, start the agent and provide one of your setup keys:
sudo apt-get install netbird
sudo netbird up --management-url https://netbird.carlpearson.net --setup-key <SETUP KEY>
Netbird provides an installation package. When you start the app, it will drop a little icon in the taskbar.
https://netbird.example.com
One aditional wrinkle is that "Quit" does not disconnect you. You have to click "Disconnect" first.
There is a Netbird iOS app on the Apple App Store.