-
Notifications
You must be signed in to change notification settings - Fork 1
Configuration
🏠 Home › Configuration
CHUB reads one YAML file on startup — config.yml under ${CONFIG_DIR} (in Docker, /config/config.yml). This page documents the top-level structure; per-module fields live on the Modules page.
The easiest way to edit is the web UI — every Settings page writes back through a validated API. If you hand-edit the file, keep it at 0600 (it holds API keys), and note that CHUB revalidates the whole file on startup: a bad field is named in the container log and CHUB refuses to start.
- Top-level blocks
- general
- auth
- tmdb
- fanart
- instances
- schedule
- notifications
- user_interface
- Secret handling
- If CHUB won't start
- Appendix: full example + legacy migration
Anything you omit falls back to safe defaults.
| Block | What it holds |
|---|---|
general |
Global toggles: logging, webhook retry tuning, Plex cache TTL, dashboard layout, first-run gate. |
auth |
Admin user (managed by the UI). |
tmdb |
TMDB API key for ID resolution (better poster matching + request links). |
fanart |
fanart.tv key for the fanart logo/background source. |
instances |
Radarr / Sonarr / Lidarr / Plex connections → see First Run. |
schedule |
When each module runs. |
notifications |
Discord / Notifiarr per module. |
user_interface |
Server-wide default theme. |
| one block per module |
poster_renamerr, border_replacerr, nohl, … → see Modules. |
Global toggles. All editable from Settings → General.
| Field | Default | Purpose |
|---|---|---|
log_level |
info |
debug | info | warning | error | critical. |
max_logs |
9 |
Rotated log files kept (1–100). |
update_notifications |
false |
Deprecated; no longer wired at runtime. |
webhook_initial_delay |
30 |
Seconds to wait after an inbound webhook before acting (0–3600). |
webhook_retry_delay |
30 |
Seconds between Plex-availability retries (1–3600). |
webhook_max_retries |
10 |
Max retries (0–100). |
webhook_secret |
"" |
Shared secret for inbound webhooks — see Webhooks. |
plex_cache_ttl_seconds |
300 |
Reuse the Plex snapshot for this long across runs (0–86400; 0 = always refresh). |
dashboard_modules |
[] |
Which module cards show, in order; empty = all. |
dashboard_sections |
[] |
Which sections show (health, modules, scheduler, quick_start); empty = all. |
dashboard_refresh_seconds |
30 |
Dashboard poll/countdown interval when live updates aren't connected (0–3600; 0 = off). |
dashboard_upcoming_limit |
5 |
Upcoming runs listed in the Scheduler panel (1–50). |
setup_completed |
false |
First-run wizard gate; set true once the wizard finishes (backfilled true for existing installs). |
duplicate_exclude_groups |
[] |
Duplicate group IDs the UI should hide. |
Gotcha: the three
webhook_*_delay/retriesvalues pluswebhook_secretare documented in full on the Webhooks page — that's their canonical home.
Managed by the web UI — don't hand-edit unless you're resetting things.
| Field | Default | Purpose |
|---|---|---|
username |
"" |
Admin login name. |
password_hash |
"" |
Bcrypt hash (set by the UI). |
jwt_secret |
"" |
Random secret signing session tokens. |
token_expiry_hours |
24 |
Session lifetime. |
To reset the admin password, see Troubleshooting → Reset the admin password.
Optional TMDB integration. When apikey is set, CHUB resolves missing tmdb_id values (via tvdb/imdb lookups) to improve poster matching, Plex GUID joins, and Unmatched-Assets request links. There's no separate toggle — match refinement is automatic when a key is present.
| Field | Default | Purpose |
|---|---|---|
apikey |
"" |
TMDB API key. |
cache_expiration |
60 |
Days to cache resolved IDs (1–3650). |
Optional fanart.tv integration — the source for logos and backgrounds in asset_renamerr. A personal key is required for the fanart source (without it, asset runs fall back to local files). Get one free at fanart.tv/get-an-api-key.
| Field | Default | Purpose |
|---|---|---|
client_key |
"" |
Personal fanart.tv API key. |
cache_expiration |
2 |
Days to cache resolved logo/background URLs (1–3650). |
Your Radarr, Sonarr, Lidarr, and Plex connections. The key under each service (radarr_main, sonarr_4k, …) is the name you reference elsewhere in config.yml.
instances:
radarr:
radarr_main:
url: http://radarr:7878
api: <api_key>
enabled: true
webhook_force_reupload: false # bypass dedup, re-push to Plex on webhook
plex:
plex_main:
url: http://plex:32400
api: <x_plex_token> # X-Plex-Token, not a login| Field | Default | Purpose |
|---|---|---|
url |
"" |
Service base URL CHUB can reach. |
api |
"" |
API key — for Plex, the X-Plex-Token. |
enabled |
true |
Whether CHUB uses this instance. |
webhook_force_reupload |
false |
On radarr/sonarr/lidarr only: webhook uploads bypass the unchanged-file dedup and re-push to Plex. |
Setting up and testing connections is covered on First Run (the canonical guide). In short: each row in Settings → Instances has a Test button — run it after adding or editing an entry.
Gotcha: CHUB refuses cloud-metadata addresses and unrouteable ranges as a safety check. Use a normal IP or hostname your container can resolve (
http://radarr:7878works on a shared Docker network).
One entry per module that should run on a schedule. Anything not listed is manual-only (run it from the dashboard or via a webhook). Settings → Schedule writes this for you.
schedule:
poster_renamerr:
type: cron
expression: "0 */4 * * *" # every 4 hours
jduparr:
type: interval
minutes: 720 # every 12 hourstype is cron (with expression) or interval (with minutes). A built-in system health probe runs every 6 hours on its own — you don't configure it.
One entry per module that should notify, plus an optional main catch-all. Two services through the UI: Discord (channel webhook) and Notifiarr (Passthrough).
notifications:
main:
discord:
bot_name: CHUB Errors
color: "#FF0000"
webhook: https://discord.com/api/webhooks/...
poster_renamerr:
notifiarr:
color: "#5865F2"
webhook: https://notifiarr.com/api/v1/notification/passthrough/<API_KEY>
channel_id: 1234567890| Field | Applies to | Purpose |
|---|---|---|
webhook |
both | Discord channel webhook URL, or Notifiarr Passthrough URL. |
bot_name |
Discord only | Overrides the webhook's default username. |
color |
both | Hex embed color. Error notifications are always red. |
channel_id |
Notifiarr | Discord channel ID for Notifiarr to post to. |
Each configured module sends one notification per run (even no-ops). The main entry is the catch-all error channel — any ERROR-level log line from any module. Each entry's Test button uses the real notification path, so a passing test means production works too.
For full Discord/Notifiarr setup steps and per-module notification content, see UI Guide and the in-app Settings → Notifications forms.
user_interface:
theme: dark # light | darkServer-wide default. Each browser also remembers its own choice, so the header toggle sticks per device.
When CHUB returns config to the UI, these fields are replaced with ******** so they don't leak into browser storage or screenshots. Redaction matches exact leaf key names:
api, api_key, apikey, client_key, access_token, refresh_token, client_secret, token, password_hash, jwt_secret, webhook_secret, and outbound notification webhook URLs.
When you save config back through the UI, any field still equal to ******** is kept as-is — editing other fields won't wipe your secrets.
Gotcha: matching is by exact key name. The
webhook_force_reuploadandwebhook_*_delayfields are not redacted (only the literalwebhookandwebhook_secretkeys are).
The container log prints which field failed validation. Fix it or remove the bad section and restart — CHUB defaults anything missing.
docker compose logs chubSee Troubleshooting for common failures.
Full example config.yml
A complete, working example with every top-level block populated and each module enabled minimally. Per-module fields are documented on Modules.
general:
log_level: info
max_logs: 9
webhook_initial_delay: 30
webhook_retry_delay: 30
webhook_max_retries: 10
webhook_secret: ""
plex_cache_ttl_seconds: 300
duplicate_exclude_groups: []
auth:
username: admin # managed by the UI; leave the rest to CHUB
password_hash: ""
jwt_secret: ""
token_expiry_hours: 24
tmdb:
apikey: ""
cache_expiration: 60
fanart:
client_key: ""
cache_expiration: 2
instances:
radarr:
radarr_main:
url: http://radarr:7878
api: <radarr-api-key>
enabled: true
sonarr:
sonarr_main:
url: http://sonarr:8989
api: <sonarr-api-key>
enabled: true
lidarr:
lidarr_main:
url: http://lidarr:8686
api: <lidarr-api-key>
enabled: true
plex:
plex_main:
url: http://plex:32400
api: <x-plex-token>
enabled: true
schedule:
poster_renamerr:
type: cron
expression: "0 */4 * * *" # every 4 hours
jduparr:
type: interval
minutes: 720 # every 12 hours
upgradinatorr:
type: cron
expression: "15 3 * * *" # daily at 03:15
notifications:
main:
discord:
webhook: https://discord.com/api/webhooks/...
health_checkarr:
notifiarr:
webhook: https://notifiarr.com/api/v1/notification/passthrough/<API_KEY>
channel_id: 1234567890
user_interface:
theme: dark # light | dark
# --- Modules (minimal subset; see the Modules page for every field) ---
poster_renamerr:
dry_run: false
apply_method: kometa # kometa | plex
action_type: hardlink # copy | move | hardlink | symlink
asset_folders: true
source_dirs: [/kometa]
destination_dir: /posters
instances:
- radarr_main
- sonarr_main
- plex_main:
library_names: ["Movies", "TV Shows"]
add_posters: true # only used when apply_method: plex
border_replacerr:
dry_run: false
source_dirs: [/posters]
destination_dir: /posters
border_width: 26 # 0–200; color-mode only
border_colors: ["#ff7300"]
holidays:
- name: 🎃 Halloween
schedule: "range(10/01-10/31)"
colors: ["#FF6600", "#000000"]
poster_cleanarr:
mode: report # bloat: report | move | remove | restore | clear | nothing
plex_path: "/plex-config/Library/Application Support/Plex Media Server/Metadata"
instances: [plex_main, radarr_main, sonarr_main]
orphan_assets_enabled: false
orphan_assets_mode: report # report | move | remove
asset_dirs: []
stale_duplicates_enabled: false
stale_duplicates_mode: report # report | move | remove
jduparr:
hash_database: /config/jduparr.db
source_dirs: [/media/movies, /media/tv]
nohl:
searches: 10
source_dirs:
- { path: /media/movies, mode: resolve } # scan | resolve
- { path: /media/tv, mode: resolve }
instances: [radarr_main, sonarr_main]
upgradinatorr:
instances_list:
- instance: radarr_main
count: 10
tag_name: chub-upgradinatorr
search_mode: upgrade
renameinatorr:
rename_folders: true
count: 100 # integer, or "" = all items
tag_name: chub-renameinatorr
instances: [radarr_main, sonarr_main]
health_checkarr:
dry_run: true # deletes ARR items — start dry
instances: [radarr_main, sonarr_main]
nestarr:
library_mappings:
- arr_instance: radarr_main
plex_instances:
- { instance: plex_main, library_names: [Movies] }
path_mapping:
- arr_path: /media/movies
local_path: /data/movies
sync_gdrive:
gdrive_sa_location: /config/gdrive-sa.json
gdrive_list:
- id: "<google-drive-folder-id>"
location: /posters/gdrive-pullAuto-migration from DAPS / older config formats
If you drop in a config.yml from DAPS (or an older CHUB layout), CHUB detects the legacy shape on load and migrates it in place. This is config-shape migration only — there's no database or match-state migration (CHUB is still a clean install; see FAQ). On startup, if legacy signals are present:
- The original is copied to
config.yml.legacy-<timestamp>.yml(kept untouched). - Migration rules run against the parsed YAML in memory.
- The migrated YAML is written back to
config.yml. - Each rule that fired is logged.
If the file already matches the current schema, none of this happens. Re-runs are idempotent.
What gets migrated
| Kind | Example |
|---|---|
| Section moves |
main.log_level → general.log_level; main.theme → user_interface.theme; empty main: dropped |
| Section removals | Top-level discord: (per-module notifications cover it) |
| Field renames |
unmatched_assets.ignore_root_folders → ignore_folders; renameinatorr.ignore_tag → ignore_tags; poster_cleanarr.source_dirs → asset_dirs; labelarr plex_instance → instance
|
| Field removals |
poster_renamerr.incremental_border_replacerr, unmatched_assets.source_dirs, poster_cleanarr.ignore_media (warnings logged) |
| Type conversions |
poster_cleanarr.dry_run: bool → mode: str; border_replacerr.holidays: dict → list
|
Verify paths after migration. The migrator rewrites only the shape of the YAML — it can't know your host mounts. Spot-check path-valued fields after first boot: sync_gdrive.gdrive_sa_location + gdrive_list[].location, poster_renamerr.source_dirs[] + destination_dir, border_replacerr.source_dirs[] + destination_dir, poster_cleanarr.asset_dirs[] + plex_path, plex_maintenance.plex_path, nohl.source_dirs[].path, jduparr.hash_database + source_dirs[].
Restore the original: stop CHUB, copy config.yml.legacy-<timestamp>.yml over config.yml, restart.