Skip to content

Configuration

chodeus edited this page Apr 29, 2026 · 7 revisions

Configuration

CHUB reads a single YAML file on startup: config.yml, kept under ${CONFIG_DIR} (inside the Docker container, that's /config/config.yml).

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 permissions at 0600. It contains API keys.
  • CHUB revalidates the whole file on startup. If validation fails, the container log tells you which field is wrong and CHUB refuses to start.

Per-module config lives on the Modules page, next to each module's description. This page covers the top-level blocks only.

Jump to:


🧭 At a glance — top-level layout

general:           {}   # global toggles
auth:              {}   # admin user (managed by the UI)
instances:         {}   # Radarr / Sonarr / Lidarr / Plex connections
schedule:          {}   # when each module runs
notifications:     {}   # Discord / Email / Apprise per module
user_interface:    {}   # theme

# one section per module — see the Modules page for fields
poster_renamerr:   {}
border_replacerr:  {}
poster_cleanarr:   {}
labelarr:          {}
jduparr:           {}
nohl:              {}
unmatched_assets:  {}
upgradinatorr:     {}
renameinatorr:     {}
health_checkarr:   {}
nestarr:           {}
sync_gdrive:       {}
plex_maintenance:  {}

Anything you omit falls back to safe defaults.


📝 Full example config.yml

A complete, working example with every top-level block populated and each module enabled minimally. Copy this as a starting point and trim or extend as needed.

general:
  log_level: info
  update_notifications: false
  max_logs: 9
  webhook_initial_delay: 30
  webhook_retry_delay: 60
  webhook_max_retries: 3
  webhook_secret: ""               # set to require shared secret on inbound webhooks
  duplicate_exclude_groups: []

auth:
  username: admin                  # managed by the UI; leave the rest to CHUB
  password_hash: ""
  jwt_secret: ""
  token_expiry_hours: 24

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:
      enabled: true
      webhook: https://discord.com/api/webhooks/...
  health_checkarr:
    email:
      enabled: true
      from: chub@example.com
      to: [you@example.com]
      smtp_host: smtp.example.com
      smtp_port: 587
      username: chub
      password: <smtp-password>

user_interface:
  theme: dark                      # light | dark

# --- Modules ---
# See the Modules page for every field. Examples below are a minimal subset.

poster_renamerr:
  dry_run: false
  action_type: hardlink
  asset_folders: true
  source_dirs: [/kometa]
  destination_dir: /posters
  instances:
    - radarr_main
    - sonarr_main
    - plex_main:
        library_names: ["Movies", "TV Shows"]
        add_posters: true

border_replacerr:
  dry_run: false
  source_dirs: [/posters]
  destination_dir: /posters
  border_width: 26                  # crops & re-applies this much; matches the TPDB 26 px standard
  skip: false                       # holiday-only mode: when true, only runs on active-holiday days
  border_colors: ["#ff7300"]
  holidays:
    - name: halloween
      schedule: "range(10/01-10/31)"
      colors: ["#FF6600", "#000000"]

poster_cleanarr:
  mode: report
  plex_path: "/plex-config/Library/Application Support/Plex Media Server/Metadata"
  instances: [plex_main]

labelarr:
  mappings:
    - app_instance: sonarr_main
      labels: [watched, favorite]
      plex_instances:
        - instance: plex_main
          library_names: ["TV Shows"]

jduparr:
  hash_database: /config/jduparr.db
  source_dirs: [/media/movies, /media/tv]

nohl:
  searches: 10
  source_dirs:
    - { path: /media/movies, mode: movie }
    - { path: /media/tv,     mode: series }
  instances: [radarr_main, sonarr_main]

unmatched_assets:
  instances: [radarr_main, sonarr_main, plex_main]

upgradinatorr:
  instances_list:
    - instance: radarr_main
      count: 10
      tag_name: chub-upgradinatorr
      ignore_tag: ignore
      search_mode: upgrade

renameinatorr:
  rename_folders: true
  count: 100
  tag_name: chub-renameinatorr
  instances: [radarr_main, sonarr_main]

health_checkarr:
  report_only: false
  instances: [radarr_main, sonarr_main, lidarr_main]

nestarr:
  library_mappings:
    - arr_instance: radarr_main
      plex_instances:
        - { instance: plex_main, library_names: [Movies] }

sync_gdrive:
  gdrive_sa_location: /config/gdrive-sa.json
  gdrive_list:
    - id: "<google-drive-folder-id>"
      location: /posters/gdrive-pull

⚙️ general

Global toggles. All editable from Settings → General.

general:
  log_level: info              # debug | info | warning | error
  update_notifications: false  # banner when a new CHUB release is out
  max_logs: 9                  # rotated log files kept per module
  webhook_initial_delay: 30    # seconds to wait after an inbound webhook before acting
  webhook_retry_delay: 60      # seconds between retries
  webhook_max_retries: 3
  webhook_secret: ""           # empty = unauthenticated; set to require a shared secret
  duplicate_exclude_groups: [] # duplicate group IDs the UI should hide

If webhook_secret is set, every inbound webhook must send X-Webhook-Secret: <secret> (or ?secret=<secret>). See Webhooks.


🔐 auth

Managed by the web UI. Don't edit unless you're resetting things.

auth:
  username: admin
  password_hash: "$2b$12$..."
  jwt_secret: "<random>"
  token_expiry_hours: 24

To reset the admin password, see First Run → Resetting the admin password.


🔌 instances

Your Radarr, Sonarr, Lidarr, and Plex connections. The key under each service is the name you'll reference elsewhere in config.yml (radarr_main, sonarr_4k, etc.).

instances:
  radarr:
    radarr_main:
      url: http://radarr:7878
      api: <api_key>
      enabled: true
  sonarr:
    sonarr_main:
      url: http://sonarr:8989
      api: <api_key>
  lidarr:
    lidarr_main:
      url: http://lidarr:8686
      api: <api_key>
  plex:
    plex_main:
      url: http://plex:32400
      api: <x_plex_token>

Plex: the api field is the X-Plex-Token, not a Plex login. Finding your token.

About URLs: CHUB refuses to connect to cloud-metadata addresses or unrouteable ranges as a safety check. Use a normal IP or hostname your container can resolve — http://radarr:7878 works if CHUB and Radarr share a Docker network.

Testing: in Settings → Instances, each row has a Test button. Run it after adding or editing an entry.


schedule

One entry per module that should run on a schedule. Anything not listed here is manual-only (triggered by you from the dashboard, or by a webhook).

schedule:
  poster_renamerr:
    type: cron
    expression: "0 */4 * * *"   # every 4 hours
  jduparr:
    type: interval
    minutes: 720                # every 12 hours

type is either cron (with expression) or interval (with minutes). Settings → Schedule has a form that writes this for you.

A built-in system health probe runs every 6 hours on its own; you don't configure it.


📣 notifications

One entry per module that should send notifications, plus an optional main entry for global notifications.

notifications:
  main:
    discord:
      enabled: true
      webhook: https://discord.com/api/webhooks/...
  poster_renamerr:
    discord:
      enabled: true
      webhook: https://discord.com/api/webhooks/...
      mention_role: "123456789"
  upgradinatorr:
    apprise:
      enabled: true
      url: "discord://..."
  health_checkarr:
    email:
      enabled: true
      from: chub@example.com
      to: [you@example.com]
      smtp_host: smtp.example.com
      smtp_port: 587
      username: chub
      password: <smtp_password>

Discord, Email, and Apprise are supported. Apprise's URL format covers dozens of other services — see the Apprise README for the catalog.


🎨 user_interface

user_interface:
  theme: dark    # light | dark

Server-wide default. Each browser also remembers its own choice, so toggling the theme in the header sticks on that device.


🔒 Secret handling

When CHUB returns your config to the UI, it replaces these fields with ******** so they don't leak into browser storage or screenshots:

  • api, api_key
  • access_token, refresh_token, token, client_secret
  • password_hash, jwt_secret, webhook_secret

When you save config back through the UI, any field still equal to ******** is kept as-is — so editing non-sensitive fields won't wipe your API keys.


🚨 If CHUB won't start

The container log prints which field failed validation. Either fix the field or remove the bad section and restart — CHUB falls back to defaults for anything missing.

docker compose logs chub

See Troubleshooting for common failures.

Clone this wiki locally