Configuration
A setting can come from four places. When more than one names the same setting, this is the order (highest wins):
CLI flag or its BIRDNET_* variable > the Settings page > /etc/birdnet/birdnet.conf > built-in default
That is one rule, and the rest of the manual means it: the Settings page says the same, and the code that does it is src/cli.rs and src/helpers/settings_overlay.rs.
- A CLI flag and its
BIRDNET_*variable are the same setting (the binary reads the variable when the flag is absent), so set one or the other. Either counts as explicit and beats the Settings page for good: a Docker station started with-e BIRDNET_SEGMENT_DURATION=30keeps 30 whatever that field of the page says. - The Settings page is the SQLite
settingstable, edited at/admin/settings; it is also the canonical place for everything that has no flag or variable — the detection confidence threshold, per-species thresholds, sensitivity, email SMTP, quarantine rules, and so on. You do not need to touch any of these to get started. Each key inbirdnet.conf(and each value passed as a flag or variable at install time) is copied into the table the first time the station sees it — insert-only, so nothing you saved on the page is ever overwritten. Once a key has a row there, editing that key in the file changes nothing: the page’s value is the one used. (That is why changingLATITUDEin the file does not move a station that has already started once: change it on the page.) Most values saved on the page take effect at the next restart. birdnet.conf(kept for BirdNET-Pi compatibility) supplies the settings the page has no field for, and the first value of those it has.- A setting with no field on the page skips that level: the flag or variable, else the file, else the default.
Environment variables & CLI flags
The full list lives in birdnet-behavior --help, and .env.example documents every variable the binary reads; a test keeps the two and the code from drifting apart. Each row shows the environment variable, the matching CLI flag, and the birdnet.conf INI key (for BirdNET-Pi compatibility).
A key the station does not read is reported rather than ignored: a birdnet.conf line such as CONFIDENC=0.90, or an environment variable such as BIRDNET_LATITUD, is named at startup in the journal and by --doctor, with the nearest real name when one is close (“did you mean CONFIDENCE?”). Before this, a misspelt key was parsed, stored and silently left at its default.
| Env var | CLI flag | birdnet.conf key | Default |
|---|---|---|---|
BIRDNET_LATITUDE / BIRDNET_LONGITUDE | --latitude / --longitude | LATITUDE / LONGITUDE | — |
BIRDNET_ALSA_DEVICE / BIRDNET_ALSA_DEVICES | --alsa-device / --alsa-devices | ALSA_CARD / ALSA_CARDS | — |
BIRDNET_PIPEWIRE_DEVICE | --pipewire-device | — | — |
BIRDNET_RTSP_URL / BIRDNET_RTSP_URLS | --rtsp-url / --rtsp-urls | RTSP_URL / RTSP_URLS | — |
BIRDNET_LISTEN | --listen | — | 0.0.0.0:8502 |
TZ | — | — | UTC in the container. An IANA zone name (Europe/Berlin); the container files detections under this zone’s local hours, and warns at start when it is unset or not a zone the image knows. quickstart.sh writes the host’s zone into .env; compose sets no default of its own. On a host, the zone comes from the system (timedatectl set-timezone). |
BIRDNET_ANALYTICS_DB | --analytics-db | ANALYTICS_DB_PATH | <database>.duckdb beside the SQLite file; --analytics-db "" turns analytics off |
BIRDNET_SKIP_MIGRATION_BACKUP | — | — | unset. Any value but empty or 0 lets a history-rewriting migration run without the backup it otherwise takes first — for a station whose disk has no room for a copy of the database, accepting that the previous timestamps cannot be recovered |
BIRDNET_RECORDING_SCHEDULE | --recording-schedule | RECORDING_SCHEDULE | all-day |
BIRDNET_SEGMENT_DURATION | --segment-duration | SEGMENT_DURATION (BirdNET-Pi’s RECORDING_LENGTH is read as this when SEGMENT_DURATION is absent or blank) | 15 |
BIRDNET_OVERLAP | --overlap | OVERLAP | 0.0 |
BIRDNET_METADATA_MODEL | --metadata-model | METADATA_MODEL_PATH | set by the installer / entrypoint |
BIRDNET_METADATA_LABELS | --metadata-labels | METADATA_LABELS_PATH | set by the installer / entrypoint |
BIRDNET_SPECIES_ALIASES | --species-aliases | SPECIES_ALIASES_PATH | — (a tab-separated legacy<TAB>canonical scientific-name map, for species the geomodel and the classifier name differently and the automatic rules cannot connect — see tuning) |
| — | — | WEATHER_PROVIDER | open-meteo; also met-no (MET Norway, keyless), pirate-weather (needs WEATHER_API_KEY) and wunderground (your own personal weather station). An unimplemented name does not start the poll — see the weather overlay |
| — | — | WEATHER_STATION_ID | — (wunderground only: the station’s Weather Underground id) |
BIRDNET_WEATHER_API_KEY_FILE | — | WEATHER_API_KEY | — (wunderground and pirate-weather; mountable from a file like the other credentials) |
BIRDNET_EBIRD_API_KEY_FILE | — | EBIRD_API_KEY | — (absent ⇒ eBird corroboration is off entirely; mountable from a file like the other credentials — see eBird) |
| — | — | EBIRD_REGION | — (empty ⇒ the station’s own LATITUDE/LONGITUDE are used, which is usually better) |
| — | — | EBIRD_DIST_KM | 25 (0–50; the radius asked about when using coordinates) |
| — | — | EBIRD_BACK_DAYS | 14 (1–30; days of eBird history to ask about) |
BIRDNET_SF_THRESH | --sf-thresh | SF_THRESH | 0.03 (no effect without a metadata model) |
BIRDNET_PRIVACY_THRESHOLD | --privacy-threshold | PRIVACY_THRESHOLD | 0.0 (off). The model’s confidence for a human sound at or above which a window is suppressed (typically 0.01–0.03). Not BirdNET-Pi’s PRIVACY_THRESHOLD, which is a percentage: use PRIVACY_RANK below for BirdNET-Pi’s rule |
BIRDNET_PRIVACY_RANK | --privacy-rank | PRIVACY_RANK | 10 (0 = off, up to 12000). BirdNET-Pi’s human-sound rule: a window, and the windows either side of it, are suppressed when a human class ranks in the model’s top N classes, read before the detection threshold. On by default, including on a station that never set it. Either this rule or PRIVACY_THRESHOLD suppresses a window. A field on the Settings page (Privacy Rank, Detection); a flag or variable other than 10 beats it, while one left at 10 is the default and yields to the page. For a BirdNET-Pi PRIVACY_THRESHOLD of p, use max(10, 60 × p) here and 0 there — see Same audio, same detections |
BIRDNET_NOISE_THRESHOLD | --noise-threshold | NOISE_THRESHOLD | 0.0 |
BIRDNET_NOISE_CLASSES | --noise-classes | NOISE_CLASSES | Dog |
| — | — | NOISE_REMEMBER_SECS | 0.0 (off; keeps suppressing the species a bark produced for this long after it — see tuning) |
BIRDNET_CONFIRMATION_LEVEL | --confirmation-level | CONFIRMATION_LEVEL | off (needs OVERLAP) |
BIRDNET_OFFSITE_BACKUP | --offsite-backup | OFFSITE_BACKUP | off; also s3, sftp and rsync (the --help text lists three) |
BIRDNET_OFFSITE_PASSPHRASE | — (deliberately no flag) | OFFSITE_PASSPHRASE | — |
BIRDNET_OFFSITE_KEEP | — | OFFSITE_KEEP | 8 |
BIRDNET_OFFSITE_S3_ENDPOINT | — | OFFSITE_S3_ENDPOINT | — |
BIRDNET_OFFSITE_S3_BUCKET | — | OFFSITE_S3_BUCKET | — |
BIRDNET_OFFSITE_S3_PREFIX | — | OFFSITE_S3_PREFIX | (none) |
BIRDNET_OFFSITE_S3_REGION | — | OFFSITE_S3_REGION | us-east-1 |
BIRDNET_OFFSITE_S3_ACCESS_KEY | — | OFFSITE_S3_ACCESS_KEY | — |
BIRDNET_OFFSITE_S3_SECRET_KEY | — (deliberately no flag) | OFFSITE_S3_SECRET_KEY | — |
BIRDNET_OFFSITE_S3_ADDRESSING | — | OFFSITE_S3_ADDRESSING | auto |
BIRDNET_OFFSITE_SFTP_HOST | — | OFFSITE_SFTP_HOST | — |
BIRDNET_OFFSITE_SFTP_PORT | — | OFFSITE_SFTP_PORT | 22 |
BIRDNET_OFFSITE_SFTP_USER | — | OFFSITE_SFTP_USER | — |
BIRDNET_OFFSITE_SFTP_DIR | — | OFFSITE_SFTP_DIR | — |
BIRDNET_OFFSITE_SFTP_IDENTITY | — | OFFSITE_SFTP_IDENTITY | — |
BIRDNET_OFFSITE_SFTP_KNOWN_HOSTS | — | OFFSITE_SFTP_KNOWN_HOSTS | beside the identity file |
BIRDNET_OFFSITE_SFTP_HOST_KEY_POLICY | — | OFFSITE_SFTP_HOST_KEY_POLICY | yes |
BIRDNET_DUPLICATE_INTERVAL_SECS | --duplicate-interval-secs | DUPLICATE_INTERVAL_SECS | 0 |
BIRDNET_NIGHT_FILTER | --night-filter | NIGHT_FILTER | off |
BIRDNET_NIGHT_MARGIN_MINS | --night-margin-mins | NIGHT_MARGIN_MINS | 60 |
BIRDNET_NIGHT_EXTRA_NOCTURNAL | --night-extra-nocturnal | NIGHT_EXTRA_NOCTURNAL | — |
BIRDNET_NOTIFY_URLS | --notify-urls | NOTIFY_URLS | — |
BIRDNET_APPRISE_URL | --apprise-url | APPRISE_URL | — |
BIRDNET_NOTIFY_CONFIDENCE | --notify-confidence | APPRISE_MIN_CONFIDENCE | 0.8 |
BIRDNET_DEADMAN_HOURS | --deadman-hours | DEADMAN_HOURS | 24 (0 = off) |
BIRDNET_BIRDWEATHER_TOKEN | --birdweather-token | BIRDWEATHER_TOKEN | — |
BIRDNET_BIRDWEATHER_URL | — | BIRDWEATHER_URL | public BirdWeather |
BIRDNET_MQTT_HOST | --mqtt-host | MQTT_HOST | — |
BIRDNET_MQTT_HA_DISCOVERY | --mqtt-ha-discovery | — | disabled |
BIRDNET_MAX_FILES_PER_SPECIES | --max-files-per-species | MAX_FILES_SPECIES | 0 |
BIRDNET_CLIP_RETENTION_DAYS | --clip-retention-days | CLIP_RETENTION_DAYS | 0 (keep forever) |
BIRDNET_DISK_PURGE_THRESHOLD | --disk-purge-threshold | DISK_PURGE_THRESHOLD | 95 (BirdNET-Pi calls it PURGE_THRESHOLD) |
| — | — | FULL_DISK | purge. What happens at that threshold: purge deletes the oldest clips of the most-recorded species (never below PURGE_SPECIES_FLOOR, never a locked one); keep stops recording, deletes nothing and records again by itself once usage is 2 points below the threshold, as BirdNET-Pi’s FULL_DISK=keep does |
BIRDNET_PURGE_SPECIES_FLOOR | --purge-species-floor | PURGE_SPECIES_FLOOR | 5 (0 = no floor) |
BIRDNET_STREAM_RETENTION_SECS | --stream-retention-secs | STREAM_RETENTION_SECS | 600 |
BIRDNET_STREAM_MAX_MB | --stream-max-mb | STREAM_MAX_MB | 512 |
BIRDNET_RAW_AUDIO_KEEP_EVERY | --raw-audio-keep-every | RAW_AUDIO_KEEP_EVERY | 0 (keep no raw audio; 1 keeps every segment, N one in N) |
CADDY_PWD / CADDY_USER | — | CADDY_PWD / CADDY_USER | CADDY_PWD auto-set on bare-metal install; sign in as admin (CADDY_USER is environment-only — see Remote access) |
BIRDNET_PRIVATE_MODE | --private-mode | PRIVATE_MODE | off (viewing is open). true puts the whole station behind the sign-in; needs CADDY_PWD, or everything but the sign-in answers 503. See Hardening |
BIRDNET_PUBLIC_ACCESS | --public-access | PUBLIC_ACCESS | — (nothing carved out). Comma-separated live_audio, share, metrics; no effect without private mode |
BIRDNET_CORS_ALLOWED_ORIGINS | — | — | — (same-origin only) |
Invalid settings are not run. On startup the station validates the configuration. If a value is out of range (e.g. a latitude outside ±90, or a malformed
RECORDING_SCHEDULE), it runs on the last configuration that passed, or — when there is none — starts web-only, recording nothing, so the dashboard can still tell you what is wrong. Both are reported in the journal and bybirdnet-behavior --doctor; run the doctor on a config before deploying it, or apply it with--apply-config, which refuses a file that does not validate.
BIRDNET_LISTENbinds all interfaces by default (0.0.0.0:8502), so the dashboard is reachable across your LAN out of the box. Viewing is open; anything that changes something requires a password — the/adminpanel, and also deleting, relabelling, reviewing or locking a detection, or saving the onboarding wizard. A fresh bare-metal install auto-generatesCADDY_PWD(usernameadmin) and prints it once in the post-install summary — change it viaCADDY_PWDinbirdnet.conf. To restrict the dashboard to the local machine, setBIRDNET_LISTEN=127.0.0.1:8502. The authentication (CADDY_PWD) and cross-origin (BIRDNET_CORS_ALLOWED_ORIGINS) settings are covered in Remote Access & Security.
What the station connects to
Two connections are made on the station’s own initiative, with no configuration:
| Host | Why | Turn it off with |
|---|---|---|
api.github.com | Once 60 s after start and every 24 h after: checks whether a newer release exists and logs the answer. It never installs anything; an update is applied with sudo bash install.sh update (a systemd station cannot replace its own binary, so the admin panel does not try). | --no-update-check / BIRDNET_NO_UPDATE_CHECK=1 |
en.wikipedia.org, upload.wikimedia.org | Downloads a photo the first time a species is detected, then serves it from the local cache for ever. | --image-cache-dir "" |
One more can happen once, on a first run: if the bundled
duckdb-behavioral extension cannot be loaded from the local cache or the
copy embedded in the binary, DuckDB tries the community registry
(extensions.duckdb.org). Release binaries carry the extension, so this
normally never fires.
Everything else is off until you configure it, and configuring it is the
consent: Apprise, BirdWeather, MQTT, SMTP e-mail, the heartbeat ping, and the
weather poll (api.open-meteo.com by default; api.met.no, api.weather.com
or api.pirateweather.net for the other WEATHER_PROVIDERs — itself opt-in via
BNB_WEATHER_ENABLED=1).
The location button calls ipwho.is (and get.geojs.io if that does not
answer), over TLS and only when you press it. The request tells that service
this station’s public IP address, which is how it finds the area; no station
data is sent. You review the coordinates before saving them.
Offline mode
BIRDNET_OFFLINE=1 # or: --offline
Turns off both default-on connections at once — the update check and the image downloads — so the station makes no outbound connection you did not ask for. Integrations you configured explicitly keep working: offline mode is about unsolicited traffic, not about muting your alerts.
Use it on metered or cellular links, on air-gapped deployments, and wherever “what does this contact?” needs a single answer. Already-cached species images are still served; only new downloads stop.
birdnet-behavior --doctor reports the current posture under Outbound
connections, so you can confirm it rather than infer it.
Web-UI-only settings
These are stored in the SQLite settings table and have no environment variable or command-line flag; a few also have a birdnet.conf key, noted beside them. That key is copied into the settings table the first time the station sees it; after that the value saved on the page is the one the station uses. Set them at /admin/settings — see Settings & Detection.
| Setting | Where | Note |
|---|---|---|
| Detection confidence threshold | Detection | Also CONFIDENCE in birdnet.conf; default 0.70 (BirdNET-Pi’s; it was 0.75 up to v0.17.1). Per-species overrides also live here |
| Detection sensitivity (0.5–1.5) | Detection | Also SENSITIVITY in birdnet.conf; default 1.25. It means what it means in BirdNET-Pi on a V2.4 model and is ignored by V3.0 — see Settings |
| Email / SMTP notifications | Notifications | |
| Rare-bird quarantine rules | Species | |
| BirdWeather station details | BirdWeather | Token can also be set via env var |
Retention is off by default. Nothing is deleted by age unless you ask for it: set Keep Clip Audio (days) in the UI (
CLIP_RETENTION_DAYS) to reclaim the audio of older detections. Independently of that, the disk manager purges the oldest recordings once the disk hitsDISK_PURGE_THRESHOLD(default 95%) and keeps at mostBIRDNET_MAX_FILES_PER_SPECIESper species. Pin clips you want to keep with the lock action on any detection — locked clips are never purged by either limit.All three limits, plus the temporary streaming folder’s retention and size ceiling, can be set from Settings → General → System & Display in the web UI; you never have to edit a config file to change them. A value given on the command line or as a
BIRDNET_*variable wins over the UI, which in turn wins over the config file (the order at the top of this page).When a clip is purged the detection itself is kept — its filename and the date the audio was reclaimed stay on the record, so counts, species lists, trends and exports are unaffected. Only the audio goes.