docs(macos): add CLI-only binary install and MDM deployment guide (#713)

* docs(macos): add CLI-only binary install and MDM deployment guide

Document the previously undocumented binary-only install path for macOS
using USE_BIN_INSTALL and SKIP_UI_APP flags. Add a new guide for
building custom CLI-only .pkg packages for MDM fleet deployment,
covering pkgbuild workflow, pre/postinstall scripts, setup key
enrollment, and update strategies.

* docs(macos): add warnings about unsigned binaries in tarballs

The darwin tarballs from GitHub releases contain unsigned binaries —
only the official .pkg goes through the sign pipeline. Add warnings
to both the install page and MDM guide, and document the full signing
workflow: codesign for the binary, pkgbuild --sign for the package,
and optional notarization via notarytool.
This commit is contained in:
shuuri-labs
2026-05-04 10:07:39 +02:00
committed by GitHub
parent a4888e5bdd
commit a293b8a17b
3 changed files with 441 additions and 22 deletions

View File

@@ -461,6 +461,10 @@ export const docsNavigation = [
title: 'MDM for Deployment',
isOpen: true,
links: [
{
title: 'macOS CLI-Only .pkg',
href: '/manage/integrations/mdm-deployment/macos-cli-pkg-deployment',
},
{
title: 'Deploy with Jamf Pro',
href: '/manage/integrations/mdm-deployment/jamf-pro-netbird-integration',

View File

@@ -1,4 +1,4 @@
import {Note} from "@/components/mdx";
import {Note, Warning} from "@/components/mdx";
# MacOS Installation
@@ -10,6 +10,8 @@ The NetBird client (agent) allows a peer to join a pre-existing NetBird deployme
curl -fsSL https://pkgs.netbird.io/install.sh | sh
```
This installs the full NetBird application, including the desktop UI and the daemon service.
### Package install
1. Download the latest MacOS release installer for your [processor](https://support.apple.com/en-us/HT211814 ):
@@ -47,41 +49,66 @@ brew unlink netbird
sudo netbird service start
```
### Binary Install
**Installation from binary (CLI only)**
### CLI-only install (binary)
If you need only the CLI client without the desktop UI — for example, on headless servers or for MDM/fleet deployments — you can install the standalone binary directly.
#### One-command binary install
<Warning>
The macOS binary tarballs from GitHub releases are **not Apple code-signed or notarized**. Only the official `.pkg` installer from [pkgs.netbird.io](https://pkgs.netbird.io) contains signed binaries. The unsigned binary will trigger Gatekeeper warnings and may be blocked by MDM policies. For managed fleet deployments, see [Building a CLI-Only .pkg for MDM Deployment](/manage/integrations/mdm-deployment/macos-cli-pkg-deployment) which includes steps for signing the binary with your own Developer ID.
</Warning>
The install script supports a binary-only mode that downloads the tarball, extracts the `netbird` binary to `/usr/local/bin/`, and registers the launchd daemon:
```bash
curl -fsSL https://pkgs.netbird.io/install.sh | USE_BIN_INSTALL=true SKIP_UI_APP=true sh
```
To update an existing binary install, add the `UPDATE_NETBIRD` flag:
```bash
curl -fsSL https://pkgs.netbird.io/install.sh | USE_BIN_INSTALL=true SKIP_UI_APP=true UPDATE_NETBIRD=true sh
```
#### Manual binary install
1. Checkout NetBird [releases](https://github.com/netbirdio/netbird/releases/latest)
2. Download the latest release:
2. Download the latest release for macOS:
```bash
curl -L -o ./netbird_<VERSION>.tar.gz https://github.com/netbirdio/netbird/releases/download/v<VERSION>/netbird_<VERSION>_<OS>_<Arch>.tar.gz
# For Apple Silicon (M1, M2, M3, M4):
curl -L -o ./netbird.tar.gz https://github.com/netbirdio/netbird/releases/download/v<VERSION>/netbird_<VERSION>_darwin_arm64.tar.gz
# For Intel:
curl -L -o ./netbird.tar.gz https://github.com/netbirdio/netbird/releases/download/v<VERSION>/netbird_<VERSION>_darwin_amd64.tar.gz
# Universal binary (works on both architectures):
curl -L -o ./netbird.tar.gz https://github.com/netbirdio/netbird/releases/download/v<VERSION>/netbird_<VERSION>_darwin_all.tar.gz
```
<Note>
You need to replace some variables from the URL above:
- Replace **VERSION** with the latest released version.
- Replace **OS** with "linux", "darwin" for MacOS or "windows"
- Replace **Arch** with your target system CPU architecture
Replace **VERSION** with the latest released version (e.g., `0.36.5`). You can find version numbers on the [releases page](https://github.com/netbirdio/netbird/releases/latest).
</Note>
3. Decompress
3. Extract and install the binary:
```bash
tar xzf ./netbird_<VERSION>.tar.gz
sudo mv netbird /usr/bin/netbird
sudo chown root:root /usr/bin/netbird
sudo chmod +x /usr/bin/netbird
tar xzf ./netbird.tar.gz
sudo mv netbird /usr/local/bin/netbird
sudo chown root:wheel /usr/local/bin/netbird
sudo chmod +x /usr/local/bin/netbird
```
After that you may need to add /usr/bin in your PATH environment variable:
````bash
export PATH=$PATH:/usr/bin
````
4. Install and run the service
4. Install and start the daemon service:
```bash
sudo netbird service install
sudo netbird service start
```
This creates a launchd daemon at `/Library/LaunchDaemons/netbird.plist` that runs as root (required for managing the WireGuard network interface).
<Warning>
Automatic updates via the NetBird dashboard do **not** work for binary-only installs. The auto-updater requires the official `.pkg` installer (it checks for the `io.netbird.client` package receipt). For binary installs, you must update manually or push updates through your MDM solution. See [Building a CLI-Only .pkg for MDM Deployment](/manage/integrations/mdm-deployment/macos-cli-pkg-deployment) for a managed approach.
</Warning>
## Running NetBird with SSO Login
### Desktop UI Application
If you installed the Desktop UI client, you can launch it and click on Connect.

View File

@@ -0,0 +1,388 @@
import {Note, Warning} from "@/components/mdx";
# Building a CLI-Only .pkg for macOS MDM Deployment
When deploying NetBird across a fleet of macOS devices using an MDM solution (Jamf Pro, Kandji, Munki, etc.), you may want a headless, CLI-only installation without the desktop UI. The official `.pkg` from [pkgs.netbird.io](https://pkgs.netbird.io) bundles the full NetBird.app, which includes the menu bar UI. This guide shows you how to build a custom `.pkg` that installs only the `netbird` CLI binary and registers it as a launchd daemon.
This approach is useful when:
* You are deploying to headless servers or CI/CD runners that have no user session
* You want to minimize the footprint on managed endpoints
* Your MDM workflow requires a `.pkg` artifact (rather than a raw binary or install script)
* You need to control exactly which version is deployed across your fleet
## Prerequisites
* A Mac with `pkgbuild` available (included with Xcode Command Line Tools)
* Access to your MDM solution with permissions to upload packages and create deployment policies
* A NetBird [setup key](/manage/peers/register-machines-using-setup-keys) for initial enrollment (reusable keys are recommended for fleet deployments)
## How the CLI-only install works
The standard NetBird `.pkg` installer places `NetBird.app` into `/Applications` and symlinks the binary to `/usr/local/bin/netbird`. A CLI-only install skips the app bundle entirely and places the `netbird` binary directly in `/usr/local/bin/`.
When you run `netbird service install`, it creates a launchd daemon plist at `/Library/LaunchDaemons/netbird.plist`. The service runs as root, which is required for creating and managing the WireGuard network interface (`utun` device).
<Note>
The NetBird daemon stores its configuration in `/var/lib/netbird/` by default. Once a peer is enrolled (via SSO or setup key), the configuration persists across reinstalls. Subsequent package updates only need to restart the service — no re-enrollment is required.
</Note>
## Step 1: Download the CLI binary
Download the appropriate tarball from the [NetBird GitHub releases](https://github.com/netbirdio/netbird/releases/latest) page. Each release publishes architecture-specific and universal macOS binaries:
```bash
# Set the version you want to deploy
VERSION="0.36.5"
# Universal binary (recommended — works on both Intel and Apple Silicon):
curl -L -o netbird.tar.gz \
"https://github.com/netbirdio/netbird/releases/download/v${VERSION}/netbird_${VERSION}_darwin_all.tar.gz"
# Or architecture-specific:
# Apple Silicon: netbird_${VERSION}_darwin_arm64.tar.gz
# Intel: netbird_${VERSION}_darwin_amd64.tar.gz
```
Extract the binary:
```bash
mkdir -p pkg-root/usr/local/bin
tar xzf netbird.tar.gz -C pkg-root/usr/local/bin netbird
chmod +x pkg-root/usr/local/bin/netbird
```
<Warning>
The macOS binary tarballs from GitHub releases are **not Apple code-signed or notarized**. Only the official `.pkg` installer from [pkgs.netbird.io](https://pkgs.netbird.io) contains signed binaries. For MDM deployments, you should sign the binary with your own Developer ID Application certificate before packaging — see [Step 3](#step-3-build-the-pkg) for instructions.
</Warning>
## Step 2: Create install scripts
Create a `scripts/` directory with preinstall and postinstall scripts. These handle stopping any existing service before the upgrade and starting it after.
```bash
mkdir -p scripts
```
### Preinstall script
The preinstall script stops any running NetBird service to allow the binary to be replaced cleanly:
```bash
cat > scripts/preinstall << 'PREINSTALL'
#!/bin/sh
LOG_FILE=/var/log/netbird/client_pre_install.log
AGENT=/usr/local/bin/netbird
mkdir -p /var/log/netbird/
{
echo "=== NetBird CLI preinstall: $(date) ==="
# Check if netbird was installed with Homebrew
if command -v brew >/dev/null 2>&1; then
if brew list --formula 2>/dev/null | grep -q netbird; then
echo "NetBird was installed with Homebrew. Please use Homebrew to manage the package."
exit 1
fi
fi
# Stop the running service if it exists
if [ -f "$AGENT" ]; then
$AGENT service stop 2>/dev/null || true
fi
echo "Preinstall complete"
exit 0
} >> "$LOG_FILE" 2>&1
PREINSTALL
chmod +x scripts/preinstall
```
### Postinstall script
The postinstall script installs the launchd daemon service and optionally enrolls the peer with a setup key on first install:
```bash
cat > scripts/postinstall << 'POSTINSTALL'
#!/bin/sh
LOG_FILE=/var/log/netbird/client_post_install.log
AGENT=/usr/local/bin/netbird
mkdir -p /var/log/netbird/
{
echo "=== NetBird CLI postinstall: $(date) ==="
# Install and start the daemon service
$AGENT service install 2>/dev/null || true
$AGENT service start || {
echo "ERROR: Failed to start NetBird service"
exit 1
}
# Enroll the peer if not already configured.
# The config file exists once the peer has been enrolled at least once.
CONFIG_FILE="/var/lib/netbird/config.json"
if [ ! -f "$CONFIG_FILE" ]; then
echo "First install detected — enrolling with setup key"
# Replace YOUR_SETUP_KEY with your actual reusable setup key.
# For self-hosted deployments, add: --management-url https://your-management.example.com
$AGENT up --setup-key YOUR_SETUP_KEY
else
echo "Existing configuration found — skipping enrollment"
fi
echo "Postinstall complete"
exit 0
} >> "$LOG_FILE" 2>&1
POSTINSTALL
chmod +x scripts/postinstall
```
<Warning>
Replace `YOUR_SETUP_KEY` with an actual [reusable setup key](/manage/peers/register-machines-using-setup-keys) from your NetBird dashboard. For self-hosted deployments, also add the `--management-url` flag. Do not commit setup keys to version control — consider injecting them at build time via your CI/CD pipeline or MDM variables.
</Warning>
<Note>
On subsequent installs (updates), the postinstall script detects the existing configuration and skips enrollment. It only restarts the service so the new binary takes effect.
</Note>
## Step 3: Sign the binary and build the .pkg
The binary from the tarball is unsigned. For MDM deployment, you should sign both the binary (with `codesign`) and the installer package (with `pkgbuild --sign`). These are separate signing steps that use different certificate types.
### Sign the binary
Use your Developer ID Application certificate to code-sign the binary before packaging:
```bash
codesign --sign "Developer ID Application: Your Organization (TEAMID)" \
--options runtime \
--timestamp \
--force \
pkg-root/usr/local/bin/netbird
```
The `--options runtime` flag enables the hardened runtime (required for notarization), and `--timestamp` embeds a secure timestamp from Apple's servers.
### Build the .pkg
Use `pkgbuild` to assemble the signed binary and scripts into an installer package:
```bash
pkgbuild \
--root pkg-root \
--scripts scripts \
--identifier com.your-org.netbird-cli \
--version "${VERSION}" \
--install-location / \
--sign "Developer ID Installer: Your Organization (TEAMID)" \
netbird-cli-${VERSION}.pkg
```
<Warning>
Use a custom package identifier (e.g., `com.your-org.netbird-cli`) rather than `io.netbird.client`. The official identifier is used by the NetBird auto-updater to detect managed installs. Using it for your custom package could cause conflicts if the auto-updater is enabled on your NetBird dashboard.
</Warning>
<Note>
Binary signing uses a **Developer ID Application** certificate (`codesign`), while package signing uses a **Developer ID Installer** certificate (`pkgbuild --sign`). These are different certificates — you need both for a fully signed deployment. See [Apple's documentation](https://developer.apple.com/help/account/create-certificates/create-developer-id-certificates/) for details.
</Note>
### Optional: Notarize the .pkg
For full Gatekeeper compatibility, submit the signed `.pkg` to Apple for notarization:
```bash
xcrun notarytool submit netbird-cli-${VERSION}.pkg \
--apple-id "your@email.com" \
--team-id "TEAMID" \
--password "app-specific-password" \
--wait
# Staple the notarization ticket to the package
xcrun stapler staple netbird-cli-${VERSION}.pkg
```
## Step 4: Deploy via MDM
Upload the `.pkg` to your MDM solution and create a deployment policy. The exact steps vary by platform:
* **Jamf Pro**: Upload the package under `Settings > Computer Management > Packages`, then create a policy with appropriate triggers (enrollment, recurring check-in). See [Deploying NetBird with Jamf Pro](/manage/integrations/mdm-deployment/jamf-pro-netbird-integration) for detailed instructions.
* **Kandji**: Create a Custom App library item with the `Installer Package` option. See [Deploying NetBird with Kandji](/manage/integrations/mdm-deployment/kandji-netbird-integration) for detailed instructions.
* **Munki**: Import the `.pkg` into your Munki repository using `munkiimport` and assign it to the appropriate manifest.
* **Microsoft Intune**: Upload as a macOS LOB app. See [Deploying NetBird with Intune](/manage/integrations/mdm-deployment/intune-netbird-integration) for detailed instructions.
## Managing updates
Because the CLI-only install does not have the `io.netbird.client` package receipt, the NetBird [automatic update](/manage/peers/auto-update) feature does not work for these deployments. You must manage updates through one of these approaches:
### Push updated .pkg via MDM
Build a new `.pkg` with the updated binary (repeating Steps 1-3) and deploy it through your MDM. The preinstall script stops the running service, the new binary replaces the old one, and the postinstall script restarts the service. No re-enrollment is needed.
### Use the install script with UPDATE_NETBIRD
For machines where you can run scripts remotely (via MDM script execution or SSH), use the install script's update mode:
```bash
curl -fsSL https://pkgs.netbird.io/install.sh | USE_BIN_INSTALL=true SKIP_UI_APP=true UPDATE_NETBIRD=true sh
```
This downloads the latest binary, replaces the existing one, and restarts the service.
## Uninstalling
To remove a CLI-only NetBird install:
```bash
sudo netbird down
sudo netbird service stop
sudo netbird service uninstall
sudo rm /usr/local/bin/netbird
sudo rm -rf /var/lib/netbird/
sudo rm -rf /var/log/netbird/
```
You can wrap this in an MDM uninstall script or a preinstall script for a removal package.
## Complete build script
For convenience, here is a self-contained script that builds the CLI-only `.pkg`. It optionally signs the binary and package if signing identities are provided. Save it as `build-netbird-cli-pkg.sh`:
```bash
#!/bin/sh
# Usage: ./build-netbird-cli-pkg.sh <VERSION> <SETUP_KEY> [MANAGEMENT_URL]
#
# Optional environment variables for signing:
# APP_SIGN_ID - Developer ID Application identity (for binary signing)
# PKG_SIGN_ID - Developer ID Installer identity (for package signing)
# APPLE_ID - Apple ID email (for notarization)
# APPLE_TEAM_ID - Apple Team ID (for notarization)
# APPLE_APP_PWD - App-specific password (for notarization)
#
# Example (unsigned):
# ./build-netbird-cli-pkg.sh 0.36.5 A1B2C3D4-E5F6-7890-ABCD-EF1234567890
#
# Example (signed + notarized):
# APP_SIGN_ID="Developer ID Application: Acme Inc (TEAMID)" \
# PKG_SIGN_ID="Developer ID Installer: Acme Inc (TEAMID)" \
# APPLE_ID="dev@acme.com" APPLE_TEAM_ID="TEAMID" APPLE_APP_PWD="xxxx-xxxx-xxxx-xxxx" \
# ./build-netbird-cli-pkg.sh 0.36.5 A1B2C3D4-E5F6-7890-ABCD-EF1234567890
set -e
VERSION="${1:?Usage: $0 <VERSION> <SETUP_KEY> [MANAGEMENT_URL]}"
SETUP_KEY="${2:?Usage: $0 <VERSION> <SETUP_KEY> [MANAGEMENT_URL]}"
MANAGEMENT_URL="${3:-}"
PKG_ID="com.your-org.netbird-cli"
WORK_DIR=$(mktemp -d)
echo "Building NetBird CLI .pkg v${VERSION}..."
# Download and extract
curl -fSL -o "${WORK_DIR}/netbird.tar.gz" \
"https://github.com/netbirdio/netbird/releases/download/v${VERSION}/netbird_${VERSION}_darwin_all.tar.gz"
mkdir -p "${WORK_DIR}/root/usr/local/bin"
tar xzf "${WORK_DIR}/netbird.tar.gz" -C "${WORK_DIR}/root/usr/local/bin" netbird
chmod +x "${WORK_DIR}/root/usr/local/bin/netbird"
# Sign the binary if a signing identity is provided
if [ -n "${APP_SIGN_ID:-}" ]; then
echo "Signing binary with: ${APP_SIGN_ID}"
codesign --sign "${APP_SIGN_ID}" \
--options runtime \
--timestamp \
--force \
"${WORK_DIR}/root/usr/local/bin/netbird"
else
echo "WARNING: No APP_SIGN_ID set — binary will be unsigned."
echo "Unsigned binaries trigger Gatekeeper warnings and may be blocked by MDM policies."
fi
# Build management URL flag
MGMT_FLAG=""
if [ -n "$MANAGEMENT_URL" ]; then
MGMT_FLAG="--management-url ${MANAGEMENT_URL}"
fi
# Create scripts
mkdir -p "${WORK_DIR}/scripts"
cat > "${WORK_DIR}/scripts/preinstall" << 'EOF'
#!/bin/sh
AGENT=/usr/local/bin/netbird
mkdir -p /var/log/netbird/
{
echo "=== NetBird CLI preinstall: $(date) ==="
if command -v brew >/dev/null 2>&1; then
if brew list --formula 2>/dev/null | grep -q netbird; then
echo "NetBird installed via Homebrew. Aborting."
exit 1
fi
fi
[ -f "$AGENT" ] && $AGENT service stop 2>/dev/null || true
echo "Preinstall complete"
exit 0
} >> /var/log/netbird/client_pre_install.log 2>&1
EOF
cat > "${WORK_DIR}/scripts/postinstall" << EOF
#!/bin/sh
AGENT=/usr/local/bin/netbird
mkdir -p /var/log/netbird/
{
echo "=== NetBird CLI postinstall: \$(date) ==="
\$AGENT service install 2>/dev/null || true
\$AGENT service start || { echo "ERROR: Failed to start service"; exit 1; }
if [ ! -f "/var/lib/netbird/config.json" ]; then
echo "First install — enrolling with setup key"
\$AGENT up --setup-key ${SETUP_KEY} ${MGMT_FLAG}
else
echo "Existing config found — skipping enrollment"
fi
echo "Postinstall complete"
exit 0
} >> /var/log/netbird/client_post_install.log 2>&1
EOF
chmod +x "${WORK_DIR}/scripts/preinstall" "${WORK_DIR}/scripts/postinstall"
# Build the package
PKG_SIGN_FLAGS=""
if [ -n "${PKG_SIGN_ID:-}" ]; then
echo "Signing package with: ${PKG_SIGN_ID}"
PKG_SIGN_FLAGS="--sign ${PKG_SIGN_ID}"
fi
pkgbuild \
--root "${WORK_DIR}/root" \
--scripts "${WORK_DIR}/scripts" \
--identifier "${PKG_ID}" \
--version "${VERSION}" \
--install-location / \
${PKG_SIGN_FLAGS} \
"netbird-cli-${VERSION}.pkg"
# Notarize if credentials are provided
if [ -n "${APPLE_ID:-}" ] && [ -n "${APPLE_TEAM_ID:-}" ] && [ -n "${APPLE_APP_PWD:-}" ]; then
echo "Submitting for notarization..."
xcrun notarytool submit "netbird-cli-${VERSION}.pkg" \
--apple-id "${APPLE_ID}" \
--team-id "${APPLE_TEAM_ID}" \
--password "${APPLE_APP_PWD}" \
--wait
xcrun stapler staple "netbird-cli-${VERSION}.pkg"
echo "Notarization complete."
fi
# Clean up
rm -rf "${WORK_DIR}"
echo "Built: netbird-cli-${VERSION}.pkg"
echo "Upload this package to your MDM solution for deployment."
```