Configuration reference¶
Boomarr is configured with a single YAML file, boomarr.yml, inside the
config directory (/config in Docker, ./config otherwise). If the file
does not exist, a commented template is created on first start.
Unknown or misspelled options never prevent startup, but are logged as a
warning (e.g. Unknown config option 'libraries[0].symlink_libraries[0].filters[0].langauges').
- Minimal example
- Top-level options
- Libraries
- Symlink libraries
- Filters
- Triggers (watch mode)
- HTTP server: webhooks, metrics, status
- Removal guard
- Notifications
- Media server refresh
- Probers
- Database (probe cache)
- Logging
- Environment variables and CLI options
- Output directory naming
- Validation rules
Minimal example¶
output_path: /media/filtered
libraries:
- name: Movies
input_path: /media/movies
symlink_libraries:
- filters:
- type: audio_language
languages: [deu]
This creates /media/filtered/movies-deu, mirroring the folder structure of
/media/movies but containing only (symlinks to) files with a German audio
track.
Top-level options¶
| Option | Default | Description |
|---|---|---|
output_path |
– | Base directory for all symlink libraries. Required unless every library (or every symlink library) sets its own output_path. |
libraries |
[] |
List of libraries. |
triggers |
[schedule] |
Triggers for boomarr watch. |
sidecar_extensions |
[.srt, .ass, .ssa, .sub, .idx, .vtt, .sup, .smi] |
External files that are linked together with a matching media file when their name starts with the media file's name (Movie.mkv → Movie.de.srt, Movie.en.forced.ass). Set [] to disable. |
ignore_patterns |
see below | fnmatch patterns of file and directory names that are skipped while scanning. |
relative_symlinks |
false |
Create relative symlinks (../../movies/Film/Film.mkv) instead of absolute ones. See media servers. |
removal_guard |
50 % / 20 | Protection against mass removals. |
server |
disabled | HTTP server for webhooks, metrics and status. |
notifications |
none | Apprise notifications. |
media_servers |
[] |
Plex/Jellyfin/Emby refresh after changes. |
probe_workers |
4 |
Number of files probed in parallel (1–64). Only new or changed files are probed. Lower it for slow spinning disks, raise it for SSD/NVMe. |
probers |
[ffprobe] |
Probers used to read audio tracks. |
pre_probe_filters |
[file_extension] |
Cheap filters applied before probing. |
database |
sqlite |
Probe cache. |
watch.debounce |
2.0 |
Seconds to wait for more trigger events before a scan starts. |
general.tz |
UTC |
Timezone for log timestamps (also TZ). |
logging |
See logging. |
Default ignore_patterns:
ignore_patterns:
- ".*" # hidden files/dirs, macOS "._" files, .Trash-*
- "@eaDir" # Synology thumbnails
- "#recycle" # Synology recycle bin
- "#snapshot" # Synology snapshots
- "$RECYCLE.BIN"
- "System Volume Information"
- "lost+found"
- "Plex Versions" # Plex optimized versions
Setting ignore_patterns replaces the list; copy the defaults if you only
want to add a pattern.
Libraries¶
A library is a source directory that is scanned recursively.
libraries:
- name: Movies # required, unique, used for folder names
input_path: /media/movies # required, must be mounted read-only
output_path: /media/filtered # optional, overrides the global output_path
symlink_libraries: [...] # required, at least one
# Optional per-library overrides of the global options:
probers: [ffprobe]
pre_probe_filters: [file_extension]
sidecar_extensions: [.srt]
ignore_patterns: [".*", "Extras"]
relative_symlinks: true
Symlink libraries¶
Each symlink library is one output folder with its own filters. A file is linked if it passes all filters of the symlink library.
symlink_libraries:
# Automatic name: <output_path>/<library-name-slug>-<filter suffixes>
- filters:
- type: audio_language
languages: [deu]
# Fixed folder name inside the library's output path
- name: English
filters:
- type: audio_language
languages: [eng]
# Fully custom location
- output_path: /media/kids
filters:
- type: audio_language
languages: [deu]
Filters¶
audio_language¶
Matches files by the languages of their audio tracks.
- type: audio_language
languages:
- deu # shorthand for {code: deu}
- code: eng
aliases: [und] # tracks tagged "und" (undetermined) count as English
mode: any # any (default) | all
suffix: german-english # optional, overrides the automatic folder suffix
| Option | Default | Description |
|---|---|---|
languages |
– | At least one language. Plain strings or {code, aliases}. |
mode |
any |
any: at least one configured language is present. all: every configured language (or one of its aliases) is present, e.g. for "original + German dub" libraries. |
suffix |
joined codes | Folder suffix used for automatic output naming. |
Language codes are normalised. Matroska files store ISO 639-2/B codes
(ger, fre, dut), other containers often use ISO 639-2/T (deu, fra,
nld), ISO 639-1 (de) or IETF tags (de-DE). Boomarr treats all of these
as the same language, both in your config and in the files, so deu, ger,
de and de-DE are interchangeable.
Tracks without a language tag are reported as und. Add und as an alias if
your untagged files are in a known language.
resolution¶
- type: resolution
min_height: 2160 # or "4k", "uhd", "1080p", "fhd", "720p", "hd", "sd"
max_height: 2160 # optional upper bound
The resolution is compared as its 16:9 equivalent, so cropped "scope"
releases (1920×800) count as 1080p. A tolerance of 5 % applies. Automatic
suffix: 2160p-plus, max-720p or 720p-1080p.
video_codec / audio_codec¶
- type: video_codec
codecs: [hevc, av1] # h265/x265 = hevc, avc/x264 = h264
- type: audio_codec
codecs: [truehd, eac3, dts] # e-ac-3 = eac3, ac-3 = ac3
A file matches if at least one video (resp. audio) track uses one of the codecs. Suffix: the sorted codec names.
audio_channels¶
- type: audio_channels
min_channels: 6 # 5.1 or more
invert¶
Every filter accepts invert: true to negate it, e.g. "everything that has
no English audio track":
- type: audio_language
languages: [eng]
invert: true # automatic suffix: not-eng
Filters of a symlink library are combined with AND, so for example "German 4K with surround sound" is:
- name: German UHD
filters:
- {type: audio_language, languages: [deu]}
- {type: resolution, min_height: 4k}
- {type: audio_channels, min_channels: 6}
file_extension (pre-probe)¶
Only files with these extensions are probed. Case and leading dot do not matter.
pre_probe_filters:
- type: file_extension
extensions: [mkv, mp4, avi, m4v, ts, wmv, flv, mov, webm] # default
Triggers (watch mode)¶
boomarr watch runs until stopped and scans whenever a trigger fires. All
triggers share one queue; events arriving within watch.debounce seconds
are collapsed into one scan. Unchanged files are served from the probe
cache, so frequent scans are cheap.
schedule¶
triggers:
- type: schedule
interval: 600 # seconds between scans (default 600)
run_on_start: true # scan immediately on startup (default true)
HTTP server: webhooks, metrics, status¶
boomarr watch can run a small built-in HTTP server:
server:
enabled: true
host: 0.0.0.0 # default
port: 9797 # default
api_key: change-me # optional, falls back to BOOMARR_API_KEY
metrics_auth: false # require the API key for /metrics too
| Endpoint | Auth | Description |
|---|---|---|
GET /health |
no | Liveness check, returns {"status": "ok"}. |
GET /metrics |
if metrics_auth |
Prometheus metrics. |
GET /api/v1/status |
yes | Last scan result as JSON. |
POST /api/v1/scan |
yes | Queue a full scan. |
POST /api/v1/webhook/<name> |
yes | Same, <name> (e.g. sonarr) is only used for logging. |
The API key can be sent as X-Api-Key header, ?apikey= query parameter,
Authorization: Bearer <key>, or as the password of HTTP basic auth.
The latter is what Sonarr/Radarr offer:
Sonarr/Radarr → Settings → Connect → + → Webhook - URL:
http://boomarr:9797/api/v1/webhook/sonarr- Method:POST- Password: your API key (username can be anything) - Triggers: On File Import, On File Upgrade, On Rename, On Delete
Without an API key anyone who can reach the port can trigger scans (they cannot do anything else). Keep the port on an internal network.
Metrics include boomarr_scans_total{status}, boomarr_scan_duration_seconds,
boomarr_last_scan_timestamp_seconds, boomarr_links{output},
boomarr_links_created_total, boomarr_links_removed_total,
boomarr_files_probed_total, boomarr_errors_total,
boomarr_removal_guard_blocked_total, boomarr_cache_entries and
boomarr_build_info. A useful alert:
time() - boomarr_last_scan_timestamp_seconds > 3600.
The older triggers: [{type: webhook, ...}] form still works and is turned
into a server section (a deprecation warning is logged).
Removal guard¶
Mass removals are almost always an accident (a share mounted empty, a
wrong path, a filter typo). A scan therefore refuses to remove symlinks from
an output directory when it would remove more than min_count links and
more than max_percent of them:
removal_guard:
max_percent: 50 # default; 100 disables the guard
min_count: 20 # default; small changes are always allowed
The scan logs an error, counts the output as blocked (metric
boomarr_removal_guard_blocked_total, notification) and leaves the links
alone; new links are still created. If the removals are intended, e.g.
after changing filters, run once:
docker exec boomarr boomarr scan --force
Notifications¶
Boomarr sends notifications through Apprise, which supports Telegram, Discord, Slack, Matrix, ntfy, Gotify, Pushover, e-mail and ~100 more services.
notifications:
urls: # or BOOMARR_NOTIFY_URLS (whitespace separated)
- tgram://bottoken/ChatID
- ntfys://ntfy.sh/my-boomarr
on_errors: true # default: scan failed or files could not be probed
on_blocked: true # default: the removal guard blocked a removal
on_changes: false # links were created or removed
Media server refresh¶
After a scan changed an output directory, Boomarr can tell your media server to rescan exactly that folder instead of waiting for its schedule:
media_servers:
- type: plex
url: http://plex:32400
token: your-plex-token # https://support.plex.tv/articles/204059436
- type: jellyfin # or: emby
url: http://jellyfin:8096
api_key: your-api-key # Dashboard → API Keys
# Only needed if the media server sees the output under another path:
path_mappings:
- local: /data/filtered # path inside Boomarr
remote: /media/filtered # same folder inside the media server
- Plex: every library section whose folder contains (or is inside) a changed output directory gets a partial scan of that folder.
- Jellyfin/Emby: the changed folders are reported via
/Library/Media/Updated, which triggers a targeted scan.
Failures are logged and never affect the scan.
Probers¶
Probers read the audio tracks of a file. They form a fallback chain: the first prober that returns a result wins.
probers:
- type: ffprobe
path: ffprobe # executable name or absolute path
timeout: 60 # seconds per file
Boomarr checks at startup that ffprobe is available and exits with a clear error otherwise. The Docker image ships a static ffprobe build.
Sonarr and Radarr probers¶
Sonarr and Radarr already know the audio languages of every file they imported. Asking them is much faster than probing, especially on network storage:
libraries:
- name: Movies
input_path: /data/media/movies
probers:
- type: radarr
url: http://radarr:7878
api_key: your-radarr-api-key
path_mappings: # only if Radarr uses other paths
- local: /data/media/movies
remote: /movies
cache_ttl: 300 # seconds the library index is reused
- ffprobe # fallback for files Radarr doesn't know
symlink_libraries: [...]
Boomarr fetches the whole library in one request (Sonarr: one per series),
caches it for cache_ttl seconds and maps paths with path_mappings. Files
Sonarr/Radarr do not know, or whose languages are empty, go to the next
prober. If the server is unreachable, Boomarr logs a warning and falls back
as well.
Database (probe cache)¶
database:
type: sqlite # default
dir: /config # default: the config directory
file_name: boomarr.db
or
database:
type: memory # nothing persisted, every restart probes everything
The cache stores the audio tracks of every probed file keyed by path, size and modification time. Filters are never cached: changing languages, adding a symlink library or deleting links by hand takes effect on the next scan without probing a single file again. Entries of deleted files are pruned automatically. The cache can be deleted at any time; the next scan simply re-probes all files.
Logging¶
logging:
level: info # debug | info | warning | error | critical
color: true # only applied when stderr is a terminal
format: "%(asctime)s | %(levelname)-8s | %(name)s: %(message)s"
date_format: "%Y-%m-%d %H:%M:%S"
file_name: boomarr.log # empty string disables file logging
rotation:
enabled: true
max_bytes: 10485760
backup_count: 3
rotate_on_start: true
The log directory can only be set via LOG_DIR / --log-dir (default
<config>/logs in Docker); set it to an empty string to disable file logging.
Environment variables and CLI options¶
| Env var | CLI option | Description |
|---|---|---|
CONFIG_DIR |
--config-dir |
Config directory (/config in Docker). |
CONFIG_FILE_NAME |
--config-file-name |
Config file name (boomarr.yml). |
LOG_LEVEL |
--log-level |
Overrides logging.level. |
LOG_DIR |
--log-dir |
Log directory, empty disables file logging. |
LOG_FILE_NAME |
--log-file-name |
Log file name. |
LOG_COLOR, LOG_FORMAT, LOG_ROTATION_ENABLED, … |
Any logging.* option as LOG_<OPTION> / LOG_ROTATION_<OPTION>. |
|
TZ |
Timezone for timestamps. | |
BOOMARR_API_KEY |
API key for the HTTP server when server.api_key is not set (WEBHOOK_API_KEY also works). |
|
BOOMARR_NOTIFY_URLS |
Whitespace separated Apprise URLs when notifications.urls is not set. |
|
HEARTBEAT_FILE |
Heartbeat file used by boomarr healthcheck. |
|
PUID, PGID, UMASK |
Docker only: user, group and umask Boomarr runs as. | |
DANGEROUS_SKIP_READONLY_CHECK |
--dangerous-skip-readonly-check |
Disable the read-only source check. Development only. |
Environment variables win over the config file (a warning is logged when both are set); CLI options win over both.
Output directory naming¶
For every symlink library the output directory is, in order of precedence:
- its own
output_path, <base>/<name>whennameis set,<base>/<library name, lower-case, spaces → dashes>-<filter suffixes>,
where <base> is the library's output_path or the global output_path.
The automatic suffix of an audio_language filter is its configured codes,
lower-cased, sorted and joined with - ([eng, DEU] → deu-eng); see the
individual filters for theirs. Inverted filters are prefixed with not-.
Validation rules¶
Boomarr refuses to start with a configuration that could damage your data or produce conflicting results:
- Output directories must not be inside, equal to, or contain any library's input directory.
- Every symlink library needs its own output directory, and output directories must not be nested inside each other.
- Library names, symlink library names and suffixes must be usable as a
single folder name (no
/,\,.or..). - Library names must be unique; every library needs at least one symlink library and every symlink library at least one filter.
- Input directories must be read-only for Boomarr (checked at runtime).