You are viewing archived documentation for v0.20. Go to latest →

Hub Deployment

The hub is the central component of BirdNET-NG: an API gateway, MQTT ingester, job queue manager, and web UI host. It runs as an 8+ container Docker Compose stack behind a Traefik reverse proxy (9 with split mode).

What the Hub Does

  • API gateway (Fastify) — serves the REST API for web and mobile clients, handles JWT and API key auth
  • MQTT ingester — receives audio chunks and telemetry from satellites via Mosquitto
  • Job queue — dispatches BirdNET inference jobs to workers via Redis + BullMQ
  • Web UI — serves the React SPA via nginx, proxying /api/* to the hub process
  • Database — PostgreSQL 17 with multi-tenant row-level isolation
  • Storage — MinIO for S3-compatible audio and image blob storage

Docker Compose Deployment

Docker Compose is the primary and recommended deployment method.

Containers

Container Image Purpose
postgres PostgreSQL 17 Multi-tenant data store
redis Redis 7 BullMQ job queue backend
mosquitto Mosquitto 2.x MQTT broker (dynamic security plugin)
minio MinIO S3-compatible audio + image storage
api Node.js Hub process (API gateway)
dispatcher Node.js Background workers (split mode only: MQTT ingester, monitors, webhooks, image worker)
web nginx React SPA, proxies /api/* to hub
docs Node.js Documentation site (Express + markdown-it)
worker Python 3.11 BirdNET inference (scalable)

Networks

  • traefik (external) — Traefik routes HTTPS + WSS traffic
  • backend (internal) — Inter-container communication

Storage

All persistent data uses bind mounts under BNG_VOLUMES_ROOT (default: ./storage/):

storage/
├── postgres/    # Database files
├── redis/       # Redis AOF
├── mosquitto/   # Broker data + dynamic security DB
└── minio/       # Audio blobs + species images

Commands

# Start full stack (default mode — single hub process)
docker compose up -d

# Rebuild after code changes
docker compose up -d --build

# View logs
docker compose logs -f api

# Stop everything
docker compose down

Hub Modes (HUB_MODE)

The hub can run in three modes, controlled by the HUB_MODE environment variable:

Mode Description
full (default) Single process running the API server and all background workers. No Redis pub/sub bridge needed. This is the original behavior.
api Stateless Fastify HTTP + WebSocket server only. Can be scaled horizontally with replicas. Uses a lightweight MqttConfigPusher to push config to satellites via MQTT.
dispatcher Background worker only: MQTT ingester, satellite monitor, detection watcher, webhook dispatcher, species image worker. Must run as a single instance.

In full mode, everything runs in one process exactly as before. In split mode, api and dispatcher run as separate containers and communicate via Redis pub/sub (e.g., detection alerts are published by the dispatcher and delivered to WebSocket clients by the API).

Default Mode (unchanged)

Don't set HUB_MODE (or set it to full). The standard docker compose up -d starts a single hub process that handles everything:

docker compose up -d

Split Mode Deployment

Split mode separates the stateless API from the background workers, allowing you to scale the HTTP/WebSocket layer independently.

  1. Set HUB_MODE=api in your .env file.
  2. Start the stack with the split profile:
docker compose --profile split up -d

This starts two hub containers:

  • api (HUB_MODE=api) — handles HTTP requests and WebSocket connections
  • dispatcher (HUB_MODE=dispatcher) — runs MQTT ingestion and all background workers
  1. Scale the API layer:
docker compose --profile split up -d --scale api=3

The dispatcher must remain a single instance (it owns MQTT subscriptions, satellite monitoring, and the species image download queue). The API containers are stateless and safe to scale behind Traefik.

Note: Each API instance maintains its own MQTT connections (for credential provisioning and config push). These connections auto-reconnect and will wait up to 10 seconds for reconnection if temporarily lost. No sticky sessions or shared state is required — all API instances are fully independent.

Configuration

All runtime configuration is via .env (see .env.example for the full template). No hardcoded secrets.

FQDNs

Four FQDNs are required, each configured in .env:

Variable Purpose
BNG_APP_FQDN Web UI + API (HTTPS, nginx -> hub)
BNG_MQTT_FQDN Mosquitto broker (WSS via HTTPS :443)
BNG_S3_FQDN MinIO console (HTTPS)
BNG_DOCS_FQDN Documentation site (HTTPS)

Key Environment Variables

Variable Description
JWT_SECRET Secret for signing JWT session tokens (required)
PLATFORM_ADMIN_EMAILS Comma-separated email addresses that get platform admin access
HUB_INTERNAL_API_KEY Backwards-compatible machine access key (optional)

Traefik Configuration

Four routes are needed across two entrypoints:

Entrypoint Port Protocol Service
websecure 443 HTTPS Web UI + API (BNG_APP_FQDN)
websecure 443 HTTPS (WSS) MQTT broker via WebSocket (BNG_MQTT_FQDN)
websecure 443 HTTPS MinIO console (BNG_S3_FQDN)
websecure 443 HTTPS Documentation site (BNG_DOCS_FQDN)

The docker-compose.yml includes Traefik labels for automatic routing. No services expose ports directly to the host; everything goes through Traefik.

Database Migrations

Migrations run automatically on hub startup (see packages/hub/src/db/migrate.ts). There are currently 26 migrations (001-026).

To run migrations manually:

docker compose exec api node packages/hub/dist/db/migrate.js

Scaling Inference Workers

Workers are stateless and can be scaled independently of the hub:

docker compose up --scale worker=4 -d

See the Workers page for detailed worker deployment options.

Updating

To update the hub and all services:

cd ~/birdnet-ng
git pull
docker compose --profile split up -d --build --scale api=2 --scale worker=2

This rebuilds all containers with the latest code and restarts them. Database migrations run automatically on API startup. No data loss — PostgreSQL, Redis, and MinIO volumes are persistent.

For zero-downtime updates with scaled API instances, the load balancer (Traefik) handles rolling restarts automatically.