5.3 KiB
NetBird GitOps Reconciler — PoC Deployment
Ansible playbook that deploys a self-contained stack on VPS-A for end-to-end testing of the NetBird GitOps reconciler.
Stack overview
| Component | Purpose |
|---|---|
| Caddy | TLS termination, reverse proxy |
| NetBird | Management, Signal, Relay, Dashboard, TURN |
| Reconciler | Declarative config → NetBird API reconciler |
| Gitea | Git server for GitOps source-of-truth |
| Gitea Runner | Executes CI workflows (Actions) |
All services run as Docker containers on a single VPS, connected via a netbird
Docker bridge network. Caddy handles ACME certificates automatically.
Prerequisites
- SSH access to
46.225.220.61(root, key-based) - DNS A records:
vps-a.networkmonitor.cc→46.225.220.61gitea.vps-a.networkmonitor.cc→46.225.220.61
rsyncinstalled locally (used to sync reconciler source)- Ansible 2.15+ with
community.generalandansible.posixcollections
Install collections if needed:
ansible-galaxy collection install community.general ansible.posix
Deployment (multi-phase)
The deployment is intentionally multi-phase because some tokens can only be obtained after services are running.
Phase 1: Initial deploy
cd poc/ansible
cp group_vars/all/vault.yml.example group_vars/all/vault.yml
ansible-playbook -i inventory.yml playbook.yml
The playbook will:
- Generate secrets (encryption key, TURN password, relay secret, reconciler token)
- Install Docker, configure UFW
- Rsync the reconciler source code and build the Docker image
- Template configs and start all services
- Skip the Gitea Actions runner (no token yet)
- Print a summary with generated secrets
Save the generated secrets into vault.yml so subsequent runs are
idempotent.
Phase 2: Create NetBird admin + API token
- Open
https://vps-a.networkmonitor.ccin a browser - Create the first admin account (embedded IdP — no external OAuth)
- Go to Settings → Personal Access Tokens → Generate
- Copy the token into
vault.ymlasvault_netbird_api_token
Phase 3: Set up Gitea
- Open
https://gitea.vps-a.networkmonitor.ccand complete the install wizard - Create an admin account (user:
blastpilot) - Create org
BlastPilotand reponetbird-gitops - Generate a Gitea API token (Settings → Applications) →
vault_gitea_token - Go to Site Administration → Actions → Runners → copy runner registration
token →
vault_gitea_admin_passwordandvault_gitea_runner_token
Phase 4: Re-deploy with all tokens
ansible-playbook -i inventory.yml playbook.yml
This run will:
- Start the reconciler with a valid NetBird API token
- Register and start the Gitea Actions runner
- Wire the reconciler to poll Gitea for
netbird.jsonchanges
Phase 5: Push code and test CI
cd /path/to/netbird-gitops
git remote add poc git@gitea.vps-a.networkmonitor.cc:BlastPilot/netbird-gitops.git
git push poc main
Then configure Gitea repo secrets (Settings → Actions → Secrets):
RECONCILER_TOKEN— the reconciler bearer tokenRECONCILER_URL—https://vps-a.networkmonitor.cc/reconcilerGITEA_TOKEN— same Gitea API token
Create a branch, modify netbird.json, open a PR — the dry-run workflow should
post a plan as a PR comment.
Testing
Replace <TOKEN> with vault_reconciler_token.
Health check
curl https://vps-a.networkmonitor.cc/reconciler/health
Dry-run reconcile
curl -X POST \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d @ansible/files/netbird-seed.json \
'https://vps-a.networkmonitor.cc/reconciler/reconcile?dry_run=true'
Apply reconcile
curl -X POST \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d @ansible/files/netbird-seed.json \
'https://vps-a.networkmonitor.cc/reconciler/reconcile'
Export current state
curl -H "Authorization: Bearer <TOKEN>" \
'https://vps-a.networkmonitor.cc/reconciler/export'
Teardown
Remove all containers and volumes:
ssh root@46.225.220.61 "cd /opt/netbird-poc && docker compose down -v"
Stop the runner:
ssh root@46.225.220.61 "systemctl stop gitea-runner && systemctl disable gitea-runner"
File structure
poc/
ansible/
inventory.yml # VPS-A host definition
playbook.yml # Main deployment playbook
.gitignore # Excludes vault.yml
group_vars/
all/
vars.yml # Non-secret config (versions, ports, etc.)
vault.yml.example # Secret template — copy to vault.yml
templates/
docker-compose.yml.j2 # All services (NetBird + Gitea + Reconciler)
management.json.j2 # NetBird management config
Caddyfile.j2 # Caddy reverse proxy (NetBird + Gitea)
dashboard.env.j2 # NetBird dashboard env
relay.env.j2 # NetBird relay env
turnserver.conf.j2 # TURN server config
reconciler.env.j2 # Reconciler env
files/
netbird-seed.json # Example desired state for testing
README.md # This file