Post 10 built the plumbing. This page is about the brains: Sonarr and Radarr, and the three decisions inside them that determine whether your library is a library or a junk drawer. I’ve run these tools for a long time; what follows is the settled residue of getting each piece wrong at least once.

The mental model

A *arr is a reconciliation loop. You declare what you want (this show, this quality range), it continuously compares that against what you have, and it closes the gap: searches the indexers, hands the pick to a download client, waits, then renames and moves the result into the library. That last step is where most setups quietly rot. The *arr is the single naming authority for your library; everything in this post flows from taking that seriously.

Prowlarr sits beside them as the indexer hub: sources get configured once there and sync to every *arr. Add an indexer in one place, it appears everywhere. The alternative, maintaining indexer lists per app, is a five-minute job that multiplies into an evening.

Step 1: naming templates, set once, never touched

These pair with the Plex scanner expectations from post 15 (year, provider ID, SxxEyy). Sonarr, Settings, Media Management:

Series folder:
  {Series TitleYear} {tvdb-{TvdbId}}

Season folder:
  Season {season:00}

Episode (standard):
  {Series TitleYear} - S{season:00}E{episode:00} - {Episode CleanTitle} [{Custom Formats }{Quality Full}]

Radarr, same idea:

Movie folder:
  {Movie TitleThe} ({Release Year}) {imdb-{ImdbId}}

Movie file:
  {Movie TitleThe} ({Release Year}) {imdb-{ImdbId}} [{Custom Formats }{Quality Full}]

What each piece does: the year disambiguates remakes; the {imdb-}/{tvdb-} token makes Plex’s matching deterministic (the scanner reads the ID and stops guessing); CleanTitle strips characters that upset some filesystems and at least one TV client; the quality block in brackets is for humans, handy for seeing at a glance what an upgrade replaced.

Turn on Rename Episodes, and under Importing leave “Use Hardlinks instead of Copy” enabled. That setting only delivers if you built the /data single-mount convention from post 15. If your imports are slow copies, fix the mounts, not this checkbox.

Step 2: quality profiles that terminate

The classic failure: enable every quality from DVD to remux, allow upgrades across the whole range, and spend the year re-downloading the same titles in ever-fatter files. A profile should answer two questions: what’s acceptable now, and where does upgrading STOP.

Mine, after plenty of oscillation:

  • TV (WEB-1080p). WEBDL-1080p and WEBRip-1080p acceptable, upgrade-until WEBDL-1080p. TV gets watched once on TVs and phones; 1080p WEB is the sweet spot of quality, size, and availability.
  • Movies (HD-1080p). Bluray-1080p preferred, WEBDL-1080p acceptable, upgrade-until Bluray-1080p. No remuxes: a 60GB file whose extra bits my screens can’t show is just a slower backup.
  • 4K: a deliberate non-goal until every link in the chain (displays, storage, transcode math) says yes. Half-committing to 4K gets you a library that’s 10% huge files you transcode down anyway.

The principle: upgrades must terminate. Every profile gets an explicit upgrade-until ceiling. Once a title reaches it, the loop goes quiet. A profile without a ceiling is an agreement to churn bandwidth forever.

Step 3: custom formats, using other people’s homework

Quality alone can’t express “prefer proper releases, avoid x265 re-encodes of WEB sources, never touch these groups.” Custom formats can: they’re matchers that add or subtract score from candidate releases, and the *arr picks the highest score within the allowed qualities.

They’re also a rabbit hole. The TRaSH guides (trash-guides.info) maintain battle-tested format definitions and scoring for exactly the profiles above: unwanted-release filters, repack handling, codec steering, group lists. Use theirs. Hand-rolling custom formats is for the day their sets don’t cover your problem, and that day took years to arrive for me.

Step 4: recyclarr, so the settings live in git

Everything above is configured through web UIs, which means none of it is in the repo, none survives a rebuild, and none has a diff history. recyclarr fixes that: a small container that reads YAML and pushes TRaSH quality definitions, custom formats, scores, and naming config into Sonarr and Radarr on a schedule.

# config/recyclarr/recyclarr.yml (excerpt)
sonarr:
  main:
    base_url: http://sonarr:8989
    api_key: !env_var SONARR_API_KEY
    quality_definition:
      type: series
    custom_formats:
      - trash_ids:
          # repack/proper handling, unwanted filters, ...
        assign_scores_to:
          - quality_profile:
              name: WEB-1080p

The YAML lives in the repo, the API keys ride the sops pipeline (post 4), the sync runs nightly. Now the *arr config has the same properties as everything else in the lab: versioned, reviewable, rebuildable. When a TRaSH update changes scoring, it shows up as a diff in recyclarr’s logs instead of as mystery behavior.

The caveat: recyclarr owns what it manages. Hand-edit a custom format it controls and the next sync reverts you. That’s the two-authorities problem from post 15 in a different hat. Decide per-knob whose it is, and don’t share custody.

The end-to-end trace

One request, no humans:

1. someone adds a show in the request portal
2. portal -> Sonarr: monitored series, WEB-1080p profile
3. Sonarr -> Prowlarr-synced indexers: search
4. candidates scored by profile + custom formats
5. winner -> qBittorrent (inside gluetun, post 10)
6. download lands in /data/torrents/...
7. Sonarr imports: HARDLINK to /data/media/tv/Show (Year) {tvdb-...}/
   Season 01/Show - S01E01 - Title [WEBDL-1080p].mkv
8. webhook -> autoscan -> Plex scans one directory
9. it's on the TV before the requester's kettle boils

Steps 4 and 7 are this page, and they’re the two you can’t patch by hand later without making everything worse. Good news, everyone: you only have to set them up once.

Next, back on the main line: knowing when any of this breaks before the household does.