No description
Find a file
toby.gethin 50dd9dabda docs: expand README with tech stack, prerequisites, and detailed usage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 10:09:12 +10:00
install.sh fix: disconnect before re-enabling NetBird SSH and disable JWT auth 2026-05-22 15:36:41 +10:00
README.md docs: expand README with tech stack, prerequisites, and detailed usage 2026-06-24 10:09:12 +10:00
uninstall.sh improv: add uninstall.sh 2025-10-15 13:36:02 +11:00
update.sh fix: disconnect before re-enabling NetBird SSH and disable JWT auth 2026-05-22 15:36:41 +10:00

NetBird EdgeOS

Installs and manages the NetBird WireGuard-based VPN client on Ubiquiti EdgeOS routers. The main challenge on EdgeOS is that firmware upgrades wipe everything outside /config/, so this project stores all state, binaries, and systemd units under /config/ and uses a post-config boot script to restore them automatically after upgrades.

Tech stack

  • Language: POSIX shell (update.sh, uninstall.sh, post-config.d/netbird.sh) and Bash (install.sh)
  • Target platform: Ubiquiti EdgeOS (Debian-derived, systemd, Vyatta config system)
  • External dependencies at runtime: curl, tar, install, mktemp, systemctl (all present on stock EdgeOS)
  • No build step, no tests, no package manager

Supported hardware

Architecture uname -m Example devices
mipsle_softfloat mips EdgeRouter X, X SFP, Lite
mips64_hardfloat mips64 EdgeRouter 4, 6P, 12
arm64 aarch64 EdgeRouter 10X

Other architectures will cause the scripts to exit with an error.

Prerequisites

  • An EdgeOS router with SSH access and root/sudo privileges
  • A running NetBird management server (self-hosted) with its URL
  • A NetBird setup key for enrolling the router as a peer
  • Internet access from the router to:
    • GitHub (github.com, api.github.com) — to download NetBird releases
    • Your Gitea instance (git.launcestonit.com.au) — to download these scripts (overridable; see Environment variables)
    • Your NetBird management server — for ongoing VPN operation

Installation

Run on the router as root:

curl -fsSL https://git.launcestonit.com.au/LauncestonIT/netbird-edgeos/raw/branch/main/install.sh \
  | sudo bash -s -- setup https://netbird.yourdomain.com

Pin a specific NetBird version:

curl -fsSL https://git.launcestonit.com.au/LauncestonIT/netbird-edgeos/raw/branch/main/install.sh \
  | sudo bash -s -- setup https://netbird.yourdomain.com 0.71.4

What the install does

  1. Detects the router architecture and downloads the matching NetBird binary from GitHub Releases
  2. Installs the binary to /usr/sbin/netbird
  3. Copies install.sh, update.sh, and uninstall.sh to /config/netbird/ for future use
  4. Writes /config/netbird/mgmt.conf with the management server URL
  5. Creates a bind mount unit (var-lib-netbird.mount) so NetBird state at /var/lib/netbird persists via /config/netbird/state/
  6. Installs /config/scripts/post-config.d/netbird.sh — this is what restores NetBird after a firmware upgrade
  7. Starts the NetBird service and enables SSH access over the NetBird tunnel

Enroll the router

After installation, connect the router to your management server:

sudo netbird up \
  --setup-key YOUR_SETUP_KEY \
  --management-url https://netbird.yourdomain.com \
  --allow-server-ssh \
  --enable-ssh-root \
  --disable-ssh-auth

--disable-ssh-auth skips JWT-to-user mapping and relies on NetBird network ACLs instead, which is appropriate for single-user devices like EdgeRouters.

Updating

After enrollment, use the update script. It reads the management URL from /config/netbird/mgmt.conf automatically.

# Update everything (scripts + NetBird binary)
sudo /config/netbird/update.sh all

# Update scripts only (does not touch the NetBird binary)
sudo /config/netbird/update.sh scripts

# Update NetBird binary only
sudo /config/netbird/update.sh netbird

# Pin a specific version for this run
sudo /config/netbird/update.sh all --version 0.71.4

To permanently pin a version, edit /config/netbird/update.conf:

NETBIRD_VERSION='0.71.4'

Set it to latest (the default) to always track the latest GitHub release.

Re-deploying

Running the install script over an existing installation is safe. It updates the binary and scripts without affecting enrollment state. Existing connections will drop briefly while NetBird restarts.

Uninstalling

sudo /config/netbird/uninstall.sh

Removes the binary, systemd units, boot scripts, cached files, and all configuration/state under /config/netbird/ and /config/data/netbird/. The router's peer record remains in the NetBird management console and must be removed manually.

Environment variables

These override defaults when set in the shell before running the scripts. They are not persisted anywhere — they only affect the current invocation.

Variable Default Used by Purpose
NETBIRD_EDGEOS_BASE_URL https://git.launcestonit.com.au/LauncestonIT/netbird-edgeos/raw/branch/main install.sh, update.sh Base URL for downloading scripts. Override this if you fork the repo or mirror it elsewhere.
NETBIRD_EDGEOS_DEFAULT_VERSION latest install.sh, update.sh Default NetBird version when none is specified via CLI args or update.conf.

On-device config files

These are written to the router during install and read by the scripts on subsequent runs:

File Format Purpose
/config/netbird/mgmt.conf Shell (MANAGEMENT_URL='...') Stores the NetBird management server URL. Sourced by update.sh during script updates.
/config/netbird/update.conf Shell (NETBIRD_VERSION='...') Stores the pinned NetBird version. Sourced by update.sh. Created on first install with the default version.
/config/data/netbird/arch Plain text Cached architecture string (e.g. mipsle_softfloat). Avoids re-detection.
/config/data/netbird/version Plain text Currently installed NetBird version string. Used to skip no-op updates.

File layout

Repository (3 files):
  install.sh          Main installer / refresh script (Bash)
  update.sh           Update script for scripts and/or binary (POSIX sh)
  uninstall.sh        Teardown script (POSIX sh)

On the router after install:
  /usr/sbin/netbird                              NetBird binary (wiped on firmware upgrade)
  /config/netbird/
    install.sh                                   Cached copy of install script
    update.sh                                    Cached copy of update script
    uninstall.sh                                 Cached copy of uninstall script
    mgmt.conf                                    Management server URL
    update.conf                                  Pinned NetBird version
    state/                                       NetBird runtime state (bind-mounted to /var/lib/netbird)
    systemd/
      var-lib-netbird.mount                      Systemd bind mount unit
      netbird.service.d/
        mount.conf                               Ensures state mount before service start
        wait-for-networking.conf                 Waits for vyatta-router.service before starting
  /config/data/netbird/
    netbird.tar.gz                               Cached binary archive (survives firmware upgrades)
    arch                                         Detected architecture
    version                                      Installed version
  /config/scripts/post-config.d/netbird.sh       Boot script that restores NetBird after firmware upgrade

How it survives firmware upgrades

EdgeOS wipes everything outside /config/ on firmware upgrade. The persistence strategy:

  1. The NetBird binary archive is cached at /config/data/netbird/netbird.tar.gz
  2. All systemd unit files and config live under /config/netbird/
  3. NetBird state is stored in /config/netbird/state/, bind-mounted to /var/lib/netbird
  4. The key piece: /config/scripts/post-config.d/netbird.sh runs on every boot. After a firmware upgrade it detects that /usr/sbin/netbird is gone, re-extracts it from the cached archive, re-installs the systemd units and symlinks, and starts the service. No manual intervention needed.

External services and integrations

Service Purpose Failure impact
GitHub API (api.github.com) Resolves latest NetBird release tag Cannot install/update when version is set to latest
GitHub Releases (github.com) Downloads NetBird binary archives Cannot install or update the binary
Gitea (git.launcestonit.com.au) Downloads these management scripts Cannot install or update scripts; override with NETBIRD_EDGEOS_BASE_URL if migrating
NetBird management server VPN control plane Peers cannot connect; existing tunnels may survive briefly

There are no cron jobs, webhooks, or CI pipelines in this repo. Updates are triggered manually by running update.sh on each router.

There is no separate router inventory — the NetBird management console's peer list is the authoritative record of which routers have this installed.

The Gitea instance (git.launcestonit.com.au) is managed by the team. NetBird management console configuration (ACLs, groups, routes) is documented in Zoho Learn under the EdgeOS manual in the "NetBird router" section. Updates to routers are rolled out ad-hoc — there is no fixed schedule.

Development

There is no build or test step. To work on the scripts:

  1. Clone the repo
  2. Edit the .sh files
  3. Push to the Gitea remote — routers pull updates from the main branch URL when update.sh scripts or update.sh all is run

To test changes without pushing, copy the modified script to a router and run it directly, or override the base URL:

# Serve scripts from a local HTTP server for testing
NETBIRD_EDGEOS_BASE_URL=http://your-dev-machine:8000 sudo bash install.sh setup https://netbird.yourdomain.com

Known issues and gotchas

  • curl | bash delivery model: The install script is designed to be piped from the Gitea server. If the Gitea instance is down or unreachable from the router, installation and script updates will fail. The NetBird binary itself comes from GitHub, so those are independent failure modes.

  • No integrity verification: Downloaded NetBird binaries are not checksum-verified. The scripts trust HTTPS transport security from GitHub. If this matters to you, add checksum verification after the curl download step.

  • --disable-ssh-auth in the enrollment command: This disables NetBird's built-in SSH JWT authentication, meaning anyone who can reach the router over the NetBird network can SSH in as root. Security depends entirely on NetBird ACLs being configured correctly in the management console.

  • Every update restarts NetBird and re-enables SSH: Both install.sh and update.sh call netbird down then netbird up with SSH flags. This causes a brief connection drop. Even a no-op update (same version) triggers a service restart.

  • Significant code duplication: detect_arch(), resolve_netbird_release(), install_cached_netbird(), ensure_netbird_service(), and enable_netbird_ssh() are duplicated across install.sh, update.sh, and the embedded post-config.d/netbird.sh boot script. The boot script is written inline as a heredoc in install.sh, so changes must be made in install.sh and then re-deployed to regenerate it on the router.

  • Config files are sourced as shell: Both mgmt.conf and update.conf are sourced with . in update.sh. Malformed content in these files could execute arbitrary commands. The shell_quote function in install.sh escapes the management URL when writing mgmt.conf.

  • install.sh is Bash but update.sh is POSIX sh: The shebang difference is intentional (install uses Bash-only features like local in some contexts), but could surprise someone editing them interchangeably. Note: local is actually used in both, but it's widely supported in POSIX-ish shells on EdgeOS (Debian's dash supports it as an extension).

  • The Gitea URL is baked into the install command: If the Gitea instance moves, every router's cached install.sh still points to the old URL. You would need to either update the scripts on each router manually or set NETBIRD_EDGEOS_BASE_URL before running updates.

  • GitHub API rate limiting: The latest version lookup hits the unauthenticated GitHub API, which is rate-limited to 60 requests/hour per IP. Unlikely to be a problem unless you're updating many routers from the same NAT IP in quick succession.