Files
netbird-iac/ansible/netbird/README.md
2026-02-15 18:37:15 +02:00

7.4 KiB

NetBird Deployment

Self-hosted NetBird VPN with embedded IdP (no external SSO required).

Quick Start

cd ansible/deployments/netbird

# 1. Generate secrets
./generate-vault.sh
ansible-vault encrypt group_vars/vault.yml

# 2. Deploy
ansible-playbook playbook-ssl.yml -i inventory.yml --ask-vault-pass

# 3. Create admin (manual - see below)

# 4. Create PAT (manual - see below)

# 5. Provision groups and users
ansible-playbook setup-groups.yml -i inventory.yml --ask-vault-pass
ansible-playbook setup-users.yml -i inventory.yml --ask-vault-pass

Prerequisites

  • Ubuntu 22.04+ VPS with public IP
  • Ports 80, 443, 3478 (TCP/UDP) open
  • Ansible 2.14+

Deployment Modes

Playbook Use Case
playbook-ssl-ip.yml HTTPS with self-signed cert on IP (recommended for isolated deployments)
playbook-ssl.yml HTTPS with Let's Encrypt (requires domain)
playbook-no-ssl.yml HTTP only (not recommended)

Full Workflow

Step 1: Configure Inventory

Edit inventory.yml:

all:
  children:
    netbird_servers:
      hosts:
        netbird-vps:
          ansible_host: YOUR_SERVER_IP
          ansible_user: root
          ansible_python_interpreter: /usr/bin/python3

Step 2: Generate Secrets

./generate-vault.sh
ansible-vault encrypt group_vars/vault.yml

This creates:

  • vault_turn_password - TURN server authentication
  • vault_relay_secret - Relay server secret
  • vault_encryption_key - Embedded IdP encryption (BACK THIS UP!)
  • vault_admin_password - Initial admin password
  • vault_netbird_service_pat - Leave empty for now

Step 3: Deploy NetBird

ansible-playbook playbook-ssl-ip.yml -i inventory.yml --ask-vault-pass

Wait for deployment to complete. Dashboard will be available at https://YOUR_IP.

Step 4: Create Admin User (Manual)

  1. Open https://YOUR_IP in browser (accept certificate warning)
  2. Create admin account:
    • Email: admin@achilles.local (from group_vars/netbird_servers.yml)
    • Password: Use vault_admin_password from vault
# View password
ansible-vault view group_vars/vault.yml | grep vault_admin_password

Step 5: Create Service User & PAT (Manual)

After logging in as admin:

  1. Go to TeamService Users
  2. Click Create Service User
    • Name: Automation Service
    • Role: Admin
  3. Click on the created service user
  4. Click Create Token
    • Name: ansible-automation
    • Expiration: 365 days
  5. Copy the token (shown only once!)

Step 6: Store PAT in Vault

ansible-vault edit group_vars/vault.yml

Set:

vault_netbird_service_pat: "nbp_xxxxxxxxxxxxxxxxxxxx"

Step 7: Create Groups

ansible-playbook setup-groups.yml -i inventory.yml --ask-vault-pass

This creates:

  • Battalion groups: battalion-1-pilots, battalion-1-ground-stations, etc.
  • Dev team group: dev-team
  • Setup keys for each group
  • Access policies (battalion isolation + dev access)

Step 8: Provision Users

Edit group_vars/netbird_servers.yml to define users:

netbird_users:
  # Dev team (full access)
  - email: "vlad.stus@achilles.local"
    name: "Vlad Stus"
    role: "admin"
    auto_groups:
      - "dev-team"

  # Battalion users
  - email: "pilot1.bat1@achilles.local"
    name: "Pilot One B1"
    role: "user"
    battalion: "battalion-1"
    type: "pilot"

  - email: "gs-operator1.bat1@achilles.local"
    name: "GS Operator One B1"
    role: "user"
    battalion: "battalion-1"
    type: "ground-station"

Then run:

ansible-playbook setup-users.yml -i inventory.yml --ask-vault-pass

Dry run (preview without creating):

ansible-playbook setup-users.yml -i inventory.yml --ask-vault-pass -e "dry_run=true"

Credentials are saved to files/credentials/users-YYYY-MM-DD.yml.

User Roles

Role Permissions
owner Full control, can delete account
admin Manage users, groups, policies, setup keys
user Connect peers, view own peers

User Types (for battalion assignment)

Type Auto-Group
pilot {battalion}-pilots
ground-station {battalion}-ground-stations

Backup & Restore

Create backup (downloads to ~/achilles-backups/netbird/):

ansible-playbook backup.yml -i inventory.yml

Restore latest backup:

ansible-playbook restore.yml -i inventory.yml

Restore specific backup:

ansible-playbook restore.yml -i inventory.yml -e "backup_file=netbird-backup-20250116T120000.tar.gz"

Backups include:

  • Management SQLite database (peers, routes, policies, users)
  • Configuration files (docker-compose.yml, Caddyfile, etc.)

Cleanup

Soft cleanup (stop containers, keep data):

ansible-playbook cleanup-soft.yml -i inventory.yml

Full cleanup (remove everything including data):

ansible-playbook cleanup-full.yml -i inventory.yml

Connecting Peers

After provisioning, users connect with:

# Using setup key (for automated deployments)
netbird up --management-url https://YOUR_IP --setup-key SETUP_KEY

# Using user credentials (interactive)
netbird up --management-url https://YOUR_IP
# Then login with email/password in browser

File Structure

ansible/deployments/netbird/
├── backup.yml                # Backup management DB and config
├── cleanup-full.yml          # Remove everything
├── cleanup-soft.yml          # Stop containers only
├── generate-vault.sh         # Generate random secrets
├── group_vars/
│   ├── netbird_servers.yml   # Main configuration
│   ├── vault.yml             # Encrypted secrets
│   └── vault.yml.example     # Template for vault
├── inventory.yml             # Server inventory
├── playbook-no-ssl.yml       # HTTP deployment
├── playbook-ssl-ip.yml       # HTTPS with self-signed (IP)
├── playbook-ssl.yml          # HTTPS with Let's Encrypt
├── restore.yml               # Restore from backup
├── setup-bootstrap.yml       # Bootstrap admin (if API available)
├── setup-groups.yml          # Create groups, keys, policies
├── setup-users.yml           # Provision users
├── templates/                # Jinja2 templates
└── files/
    └── credentials/          # Generated user passwords (gitignored)

Troubleshooting

Certificate Warning

Expected for SSL-IP mode. Accept the self-signed certificate in your browser.

"Unauthenticated" after login

JWKS race condition bug in NetBird. Wait 30 seconds and try again, or restart the management container:

ssh root@YOUR_IP "cd /opt/netbird && docker compose restart management"

API returns 404 on /api/instance/setup

The bootstrap API isn't exposed in all NetBird versions. Create admin manually in the dashboard.

View logs

ssh root@YOUR_IP "cd /opt/netbird && docker compose logs -f"

Check container status

ssh root@YOUR_IP "cd /opt/netbird && docker compose ps"