Configuration

All runtime configuration is via environment variables in .env. See .env.example for the full template.

Every setting is named OTAVI_<COMPONENT>_<SETTING> (components: HUB, DB, REDIS, S3, MQTT, AUTH, SMTP, EBIRD, BACKUP, MOBILE, WORKER; the deployment identity and the four hostnames are OTAVI_APP_*, OTAVI_MQTT_FQDN, OTAVI_S3_FQDN, OTAVI_DOCS_FQDN). The only unprefixed names are the host's PUID, PGID, TZ and Compose's own COMPOSE_*.

Renamed in hub 0.56.0 (and BNG_* to OTAVI_* in hub 0.55.0). An older .env is converted in place, keys and ${...} references alike, with the original kept beside it:

scripts/env-rename.sh .env

The hub logs every old name it still finds at startup together with its replacement, and ignores the old one. Containers no longer receive the whole .env: docker-compose.yml hands each service exactly the variables it reads, so a variable that is not listed there has no effect inside a container.

FQDNs

Variable Description Example
OTAVI_APP_FQDN Web UI + API domain birdnet.example.com
OTAVI_MQTT_FQDN MQTT broker domain mqtt.birdnet.example.com
OTAVI_DOCS_FQDN Documentation site domain docs.birdnet.example.com
OTAVI_TLS_CERTRESOLVER Traefik certificate resolver for the three names (required) letsencrypt
OTAVI_PREVIOUS_APP_FQDN / _MQTT_FQDN / _DOCS_FQDN Previous names kept working during a move, with COMPOSE_FILE=docker-compose.yml:docker-compose.previous-names.yml (unset)

Three names are required, all routed through Traefik. MinIO has no public name since hub 0.78.2 (OTAVI_S3_FQDN is no longer read). See Moving to new public names.

Hub

Variable Default Description
OTAVI_HUB_MODE full Hub process mode: full (single process), api (HTTP + WebSocket only, scalable), dispatcher (background workers only, single instance)
OTAVI_HUB_PORT 3000 HTTP listen port (internal)
OTAVI_AUTH_JWT_SECRET required Secret for signing JWT session tokens
OTAVI_AUTH_PLATFORM_ADMIN_EMAILS — Comma-separated emails for hub admin access
OTAVI_HUB_ALLOW_WEAK_SECRETS — 1 lets a development hub start with placeholder or short secrets (the hub refuses otherwise and names them)
OTAVI_HUB_METRICS_TOKEN — Bearer token for the Prometheus endpoint GET /api/metrics; empty = endpoint off. See metrics
OTAVI_HUB_INTERNAL_API_KEY — Backwards-compatible machine access key
OTAVI_AUTH_MFA_ENCRYPTION_KEY — 32-byte hex key (openssl rand -hex 32) used to AES-256-GCM-encrypt stored TOTP secrets. Required once MFA is enrolled — rotating it invalidates every existing TOTP and forces re-enrollment. Without it, MFA features fail-closed at boot.
OTAVI_AUTH_WEBAUTHN_RP_ID derived WebAuthn Relying Party ID (must match the eTLD+1 the user logs in on). Defaults to OTAVI_APP_FQDN.
OTAVI_AUTH_WEBAUTHN_RP_NAME Otavi Friendly display name shown by browser passkey prompts.
OTAVI_AUTH_WEBAUTHN_ORIGIN derived Full origin URL passed to WebAuthn verification. Defaults to https://${OTAVI_APP_FQDN}.
OTAVI_HUB_LOG_LEVEL info Log level: debug, info, warn, error

PostgreSQL

Variable Default Description
OTAVI_DB_HOST localhost Database host
OTAVI_DB_PORT 5432 Database port
OTAVI_DB_NAME birdnet Database name
OTAVI_DB_USER birdnet Database user
OTAVI_DB_PASSWORD birdnet Database password

Redis

Variable Default Description
OTAVI_REDIS_HOST localhost Redis host
OTAVI_REDIS_PORT 6379 Redis port
OTAVI_REDIS_PASSWORD — Redis auth password (empty = no auth)

MQTT (Hub side)

Variable Default Description
OTAVI_MQTT_BROKER_URL mqtt://localhost:1883 Broker URL (internal)
OTAVI_MQTT_HUB_PASSWORD hub-internal-secret Hub ingester MQTT password
OTAVI_MQTT_ADMIN_USER admin Mosquitto dynamic security admin
OTAVI_MQTT_ADMIN_PASSWORD admin Mosquitto dynamic security password

MinIO

Variable Default Description
OTAVI_S3_ENDPOINT localhost MinIO host
OTAVI_S3_PORT 9000 MinIO port
OTAVI_S3_ACCESS_KEY minioadmin MinIO access key
OTAVI_S3_SECRET_KEY minioadmin MinIO secret key
OTAVI_S3_AUDIO_BUCKET birdnet-audio Audio chunk bucket
OTAVI_S3_SPECIES_BUCKET birdnet-species Species image bucket (Wikipedia + iNat thumbs and originals)
OTAVI_S3_USE_SSL false Use SSL for MinIO connection

External APIs

Variable Default Description
OTAVI_EBIRD_API_KEY — eBird API key. When set, enables "expected species near here" filtering, rare-detection effort-aware confidence (Johnston 2021), and the Expected species card on satellite detail. Free to obtain from ebird.org

Docker Compose

Variable Default Description
OTAVI_APP_VOLUMES ./storage Root directory for all persistent data
PUID — Container user ID
PGID — Container group ID

Satellite (Pi)

The Pi's own .env (packages/satellite/.env, read by systemd). Every setting is OTAVI_SAT_* since Pi 1.6.0; an older file is converted in place by packages/satellite/scripts/env-rename.sh (the hub-triggered update does it), and the app still honours the old names with a warning.

Variable Default Description
OTAVI_SAT_ID required UUID from satellite registration
OTAVI_SAT_TENANT_ID required Tenant UUID
OTAVI_SAT_MQTT_BROKER_URL mqtt://localhost:1883 Broker URL (wss:// for production)
OTAVI_SAT_MQTT_USERNAME required MQTT username (= satellite ID)
OTAVI_SAT_MQTT_PASSWORD required MQTT password (from registration)
OTAVI_SAT_CAPTURE_MODE simulate alsa, simulate, or replay
OTAVI_SAT_AUDIO_DEVICE default ALSA device name
OTAVI_SAT_SAMPLE_RATE 48000 Audio sample rate (Hz)
OTAVI_SAT_CHUNK_DURATION_MS 3000 Chunk duration (ms)
OTAVI_SAT_OVERLAP_MS 0 Overlap between chunks
OTAVI_SAT_RECORDING_PROFILE continuous Default recording profile
OTAVI_SAT_LATITUDE 0 Static latitude
OTAVI_SAT_LONGITUDE 0 Static longitude
OTAVI_SAT_GPS_MODE static static or gpsd
OTAVI_SAT_GPSD_HOST localhost gpsd daemon host
OTAVI_SAT_GPSD_PORT 2947 gpsd daemon port
OTAVI_SAT_DATA_DIR ./data Local storage directory
OTAVI_SAT_TELEMETRY_INTERVAL_MS 60000 Telemetry report interval
OTAVI_SAT_YAMNET_ENABLED true (alsa) Enable on-device YAMNet VAD gate
OTAVI_SAT_YAMNET_MIN_BIRD_PROB 0.05 Bird-likelihood threshold (overridable by hub push)
OTAVI_SAT_YAMNET_PYTHON (auto-discovered) Python executable for the YAMNet sidecar
OTAVI_SAT_REPLAY_DIR fixtures/ Directory for replay mode WAV files

Hub settings (UI)

Configured on the Hub settings page, stored in platform_settings table.

Setting Default Description
allow_self_registration true Users can register without invite
allow_tenant_creation false Logged-in users can create tenants
allow_invite_links true Admins can generate invite links
image_download_delay_seconds 0 (auto) Delay between Wikipedia image downloads. 0 derives from authenticated/unauthenticated rate tier
wikimedia_access_token — OAuth 2.0 token for Wikimedia API (raises rate limit from 500 to 10 000 req/hr)
wikimedia_contact_email — Contact email for Wikimedia User-Agent header
inaturalist_enabled false Opt-in fallback for gallery extras when Wikipedia is exhausted hub 0.29
image_license_filter ["cc0","cc-by","cc-by-sa"] License codes accepted by iNaturalist fetches; admin can widen to cc-by-nc / cc-by-nc-sa / cc-by-nc-nd. Photos with no license_code (= all rights reserved) are always rejected hub 0.29
species_extra_images_count 5 Gallery extras per species beyond the primary. No upper bound; 0 disables extras (hub 0.29 removed the hard 20 cap)
audio_storage_cap_gb 50 Soft cap (GB). When total usage crosses, next sweep purges oldest unprotected audio
audio_storage_hard_cap_gb 60 (= soft × 1.2) Hard cap (GB). Same eviction policy as soft pass — protected (keep-best + pinned) audio is never auto-deleted (v0.29 correctness pass)
audio_retention_days 30 Delete non-keep-best audio older than this
keep_best_per_species 10 Top-N detections by confidence per species, never auto-deleted
silent_chunk_retention_hours 6 Purge chunks with 0 detections after this many hours
apk_keep_last_n 5 Keep the N most recent APK builds in mobile_releases; older ones drop on next sweep
retention_enabled false Run scheduled retention sweeps every 6h
inference_mode single single = default model only / compare = also run enabled non-default models on sampled chunks (writes to shadow_detections)
inference_compare_sample_rate 10 Percentage of chunks routed through compare mode (1–100)
dismissed_catalog_ids [] Catalog template IDs admin has hidden via the × button

Tenant Settings (UI)

Configured in Tenant Settings page, stored in tenant_settings table.

Setting Default Description
confidence_high 0.7 High confidence threshold
confidence_low 0.4 Low/uncertain confidence threshold
min_confidence 0.1 Minimum display threshold
offline_timeout_minutes 5 Satellite offline detection timeout
verification_consensus 3 Votes needed for consensus
watchlist_species [] Custom species watchlist for rare alerts
heartbeat_interval_sec 30 Satellite heartbeat interval (seconds)
filter_enabled true Enable on-device audio pre-filtering (silence + YAMNet)
filter_min_rms 0 RMS silence-gate floor, 0 = off (default since hub 0.59.0: real microphones sit far under any fixed floor and YAMNet scores silence near zero). Set only to detect a dead input at a known site
yamnet_min_bird_prob 0.05 YAMNet bird-likelihood threshold (clear birds score 3.4–3.9)
outbox_soft_size_mb 5000 Below this, the satellite outbox keeps everything; above, oldest acked first
outbox_hard_size_mb 8000 Hard ceiling; above this, oldest of anything is dropped
outbox_max_age_hours 720 Final age safety valve (30 days)
show_amphibians / show_insects / show_anthropogenic / show_human_voice / show_other_animals false Per-category UI visibility (hide non-bird detections from the list)
drop_amphibian_at_satellite / drop_insect_at_satellite / drop_anthropogenic_at_satellite / drop_human_voice_at_satellite / drop_other_animal_at_satellite true Per-category YAMNet pre-upload drop (independent of show_*; overridable per satellite)