Tutorial

Security & Monitoring

VPN mesh with no open ports using NetBird on VPS

Ten clients, ten different VPS accesses, ten sets of WireGuard keys to manage by hand. For every new admin workstation or remote office, you need to reopen a firewall, redistribute a key, and update configurations on every node. This model fails at scale. NetBird 0.76 solves this problem by fully automating peer discovery, NAT traversal, and TURN relays — and its `netbird expose` command lets you make an internal service accessible from the mesh without opening a single public port on the Internet.

13 min read10 steps
Contents· The problem: managing ten manual WireGuard tunnels1/9
  1. 01The problem: managing ten manual WireGuard tunnels
  2. 02Prerequisites
  3. 03Connect a ServOrbit VPS to a NetBird network
  4. 04`netbird expose`: an internal service without a public port
  5. 05Agency use case: connecting client VPS, office, and admin workstation
  6. 06Troubleshooting: 3 common problems
  7. 07Troubleshooting: the NetBird proxy stays on "Issuing certificate…"
  8. 08Closed origin: getting the certificate through DNS-01 with lego
  9. 09Conclusion: fewer firewall rules, more real security

The problem: managing ten manual WireGuard tunnels

WireGuard is excellent — fast, modern, cryptographically solid. But its configuration model is static: each peer must know the public IP and public key of all its peers. In a fleet of ten clients with nomadic workstations, offices behind CGNAT, and VPS with variable IPs, you spend more time synchronizing wg0.conf files than doing productive work. Add a VPS, modify the peer list on nine machines. Remove a technician, revoke their key everywhere. A subnet changes? Start over. This is the operational nightmare that NetBird was designed to eliminate.

  • Automatic mesh: NetBird manages the control plane — every new peer announces itself, others discover it without manual intervention.
  • NAT and CGNAT traversal: NetBird uses STUN to establish direct peer-to-peer connections through residential and corporate NATs.
  • Automatic TURN relay: when direct connection is impossible (strict firewall, double NAT), NetBird silently switches to an encrypted relay.
  • Granular access audit: access control policies define which peers can reach which services — visible from the dashboard.
  • Open source (BSD-3 for the client, AGPLv3 for the control plane): no vendor lock-in, self-hosting possible.

Prerequisites

Before installing NetBird, verify that your infrastructure meets the following requirements.

  • A ServOrbit VPS running Ubuntu 22.04/24.04 or Debian 12, with root access.
  • A NetBird Cloud account (free for up to 5 peers) or an already-deployed self-hosted control plane.
  • Outbound ports 443 (HTTPS/WSS) and 3478 (STUN/UDP) not blocked from the VPS.
  • The Peer Expose feature enabled on your account (available on Team and higher plans, or on a self-hosted control plane).

Connect a ServOrbit VPS to a NetBird network

Here is the complete procedure for connecting a ServOrbit VPS to your NetBird mesh network.

  1. Install the NetBird agent

    On the VPS, add the official repository and install the package:
    curl -fsSL https://pkgs.netbird.io/install.sh | sh
    Verify the installed version: netbird version should display 0.76.x.

  2. Generate a setup key

    In the NetBird dashboard (app.netbird.io or your self-hosted instance), go to Setup Keys and create a reusable or one-time key. Copy it — it only appears once.

  3. Connect the peer to the control plane

    On the VPS:
    netbird up --setup-key <YOUR_SETUP_KEY>
    For a self-hosted control plane, add --management-url https://netbird.your-domain.com:443. The command returns immediately; the peer appears in the dashboard within seconds.

  4. Add the peer to a network group

    In the NetBird interface, navigate to Peers, select the new VPS and add it to the desired group (e.g. agency-clients). Access policies linked to this group apply immediately — no restart needed.

  5. Verify mesh connectivity

    From another peer already in the network:
    netbird status then ping 100.x.x.x
    A responding ping confirms the tunnel is established.

`netbird expose`: an internal service without a public port

The netbird expose command, available since version 0.66 and consolidated in the 0.76 branch, lets you make a service listening locally (on 127.0.0.1 or a private network) accessible from the Internet via NetBird's reverse proxy — without opening a single port on the VPS firewall. The created service is ephemeral: it automatically disappears when the command stops.

Example: your monitoring panel (Grafana, Netdata) listens on localhost:3000 and should only be accessible to authorized mesh members:
netbird expose --protocol http --local-address localhost:3000 --with-user-groups supervision-admins
NetBird generates a public URL at *.tunnel.netbird.io. Port 3000 on the VPS remains closed at the firewall level.

For password-protected access without SSO, add --with-password: NetBird displays a random password you share with your collaborators. Useful for emergency access to a client without an account in your NetBird tenant.

Agency use case: connecting client VPS, office, and admin workstation

Recommended NetBird topology for an agency managing ten independent clients: Create one group per client (e.g. client-acme, client-contoso). Each client's VPS is a member of its client group. Admin workstations are members of their respective client group only. An access policy links each group to itself. The agency office is in an agency-ops group with access to all client groups — the only cross-cutting peer. SSH to client VPS uses the mesh IP (100.x.x.x); no public SSH port is needed:
ufw allow in on netbird0 to any port 22 then ufw deny 22

Scroll the table

CriterionNetBird 0.76TailscaleManual WireGuard
Peer managementAutomatic (centralized control plane)Automatic (Tailscale cloud)Manual (wg0.conf files on each node)
NAT/CGNAT traversalAutomatic STUN + TURNAutomatic STUN + DERPRequires public IP or port-forward
Pricing modelFree up to 5 peers; Team/Business plans; self-hosted freeFree up to 3 users; paid plans for teamsFree (software), operational cost in time
Open sourceYes (BSD-3 client, AGPLv3 server)Client open source (BSD-3), proprietary serverYes (GPLv2 kernel, MIT userland)
Expose service without open portYes (`netbird expose`)No (Funnel available but different)Not native (manual SSH tunnel)
Audit and access policiesFull web interface, groups and policiesACL via HuJSON file, web interfaceNone — only AllowedIPs per peer

Troubleshooting: 3 common problems

1. Unreachable peer (Status: Disconnected) — Run netbird status --detail to see the ICE state. If you see no candidate pairs, the issue is network: verify outbound UDP port 3478 is open. If direct connection fails but TURN is available, NetBird switches automatically.

2. STUN/TURN blocked by corporate firewall — NetBird supports TURN over TCP/443. Configure in the dashboard: Settings → Relay → Force TCP.

3. Reading logs — On Linux with systemd:
journalctl -u netbird -f --since '5 minutes ago'
Key messages to watch: failed to connect, signal disconnected, peer connected via relay.

Troubleshooting: the NetBird proxy stays on "Issuing certificate…"

Since NetBird started offering its self-hosted reverse proxy, one symptom keeps coming back in the issue tracker: the service created in the dashboard sits on "Issuing certificate…" indefinitely and is never reachable. NetBird issue #5517, opened on 5 March 2026 and still open at its last activity on 19 October 2026, has 35 comments. The proxy log most often shows this message: acme/autocert: unable to satisfy "https://acme-v02.api.letsencrypt.org/acme/authz/…" for domain "subdomain.site.tld": no viable challenge type found. It says the certificate authority received no valid answer to its challenges; it does not say why. There is therefore no single cause: participants describe several, and only one is confirmed by the NetBird team. Here is a diagnostic order that rules causes out one at a time.

  1. Read the proxy log

    Run docker compose logs proxy | grep -i acme: the NetBird documentation points to this command for services stuck in certificate_pending or moved to certificate_failed. Note the exact error text before changing anything, because permission denied and no viable challenge type found do not have the same remedy.

  2. Check permissions on the certificates folder

    An error such as open /certs/acme_account+key…: permission denied is the only case settled by a repository collaborator, who answers that no update will fix it and that permissions are at fault. One participant reports fixing it by giving the certs folder to user 1000:1000 (chown 1000:1000) and mounting an empty folder on /var/lib/netbird. That is a reported fix for their own setup: check which user your container runs as before copying it.

  3. Verify DNS, CAA records and ports

    The name must resolve to the proxy address and no CAA record may exclude the authority in use. Port 443 must be reachable from the Internet for the tls-alpn-01 challenge, which is the default; port 80 must be too for http-01. The documentation states that Let's Encrypt validates from several locations around the world, so upstream geographic filtering can make validation fail.

  4. Rule out a CDN that does not relay the challenge

    The documentation warns that a service placed in front of the proxy, Cloudflare for example, may not pass the acme-tls/1 ALPN protocol. In that case switch to NB_PROXY_ACME_CHALLENGE_TYPE=http-01. These checks rule causes out without guaranteeing success: one participant verified DNS, Cloudflare in DNS-only mode, ports 80 and 443 open and no CAA record, then got the same error with both challenge types.

  5. Examine the Traefik in front

    If Traefik relays TLS to the proxy through a TCP router with tls.passthrough=true, check that the HTTPS entry point carries allowACMEByPass=true. The note that follows explains what this option does and what it does not prove.

  • Confirmed by the team — a permission denied error on the /certs folder comes from file system permissions, not from the proxy version; the failed to get active profile state warning about the embedded client's profile is unrelated to the proxy and not critical, and its message is to be fixed in a future release.
  • Reported by several participants — adding allowACMEByPass=true to Traefik's HTTPS entry point unblocks issuance. Several confirm it, but two others say the option was already set on their side and the blockage persists, one of them on proxy version 0.74.4.
  • Reported by a single participant, unverified — on NetBird Cloud, slow issuance on the ZeroSSL side, combined with a 5-minute timeout in Go's autocert library, would explain the failures. The author states they are reasoning from the outside, without confirmation from the team.
  • Not established — no single cause to date: the issue still carries the triage-needed label as read on 31 October 2026. Do not present any of these leads as the cause of your case before you have tested it.

The getting-started.sh script in the NetBird repository sets --entrypoints.websecure.allowACMEByPass=true on Traefik. According to Traefik's documentation, this option, disabled by default, has your own routers handle ACME TLS and HTTP challenges instead of the internal ACME router, and lets the TLS-ALPN challenge through to a TLS router that you define. Use the name of your own entry point (websecure, https…): one participant had to set it on both the HTTP and the HTTPS entry points. It does not replace reading the log.

Closed origin: getting the certificate through DNS-01 with lego

As long as the certificate depends on a tls-alpn-01 or http-01 challenge, the authority must reach your proxy from the Internet. That conflicts with an origin closed to a CDN's ranges only, with an upstream firewall you do not manage, or with geographic filtering. The dns-01 challenge removes that dependency: it proves control of the domain with a temporary TXT record created through the DNS provider's API. As of 31 October 2026 the NetBird proxy does not include it natively: the request, NetBird issue #5450, opened on 25 February 2026 with 30 comments, is still open. The NetBird documentation does describe a route, "private proxy with lego": lego obtains and renews a wildcard certificate through DNS-01, a deploy hook copies it into a shared volume, and the proxy loads it and hot-reloads it after each renewal. A participant in the issue says they set it up without difficulty and that it can also serve public services.

  • Proxy variables — NB_PROXY_WILDCARD_CERT_DIR points to the wildcard certificate folder and NB_PROXY_PRIVATE="true" enables private mode. Pin the image version: it must support both variables.
  • Private mode closes nothing on its own — NB_PROXY_PRIVATE does not disable the public listener. Remove the public ports and the ingress routes to the proxy yourself.
  • Wildcard scope — it covers only one subdomain level. A name outside its coverage falls back to the built-in ACME, which cannot succeed if the proxy is fully private.
  • Certificate exposure — public authorities record the wildcard and the base domain in Certificate Transparency logs, and a compromised wildcard key affects every name it covers.
  • Operations — use the production ACME server, since test certificates are not trusted; back up the lego and certificate volumes; plan for a single issuer or a secure distribution of the certificate pair for a cluster of several hosts.

Access control policy: in Access Control → Policies, create a rule that only allows the agency-ops group to reach port 22 on client groups. Other ports (80, 443) can be accessible to the client group itself. This granularity prevents a compromised workstation in the mesh from pivoting to all client VPS.

Conclusion: fewer firewall rules, more real security

NetBird 0.76 represents a paradigm shift for managing a distributed VPS fleet. Instead of multiplying UFW rules and static WireGuard keys, you define groups and policies once — and the control plane handles the rest. The netbird expose command goes further: it physically removes services from Internet exposure without reconfiguring the firewall. For an agency managing ten clients or a company with distributed offices, this is the highest-impact attack surface reduction per hour invested.

Need secure infrastructure for your agency?

Order your VPS on your account at the public price and stay in control of your network. See what we offer agencies.