How a run works¶
Coldarr works in four steps. report stops after scoring, plan stops after
planning, and only apply (or Apply in the web interface) goes on to move
anything.
1. Inventory¶
Check every configured tier path (exists? mounted, if
require_mount is set? how full?), then pull every movie from Radarr
and every series from Sonarr, including tags, quality profile, and
whether an active download/import is in progress for it.
2. Score¶
Each item is evaluated into one of three buckets:
protected- taggednever-move/keep-hot/etc, or has an active download/import. Never touched.hot- should stay on primary storage (marked Favorite by any Jellyfin user, recently added, a currently-airing series, or just didn't score high enough to be cold). Never moved to cold, and reclaimed back from cold when it's a Favorite.cold- safe to relocate to overflow storage, with a score used to rank which cold items move first.
See Scoring and Favorites for the rules.
3. Plan¶
Coldarr does not steer hot storage toward any usage level -
it's runoff, not a control variable, and it's fine for it to sit
however full it ends up. Every cold-scored item currently on a hot
path is a move candidate (coldest and largest first), assigned to
whichever cold-tier path has room under its target_used_percent (the
fill goal); if nothing has target room, it falls back to whatever has
room under max_used_percent (the hard ceiling, never crossed either
way). Among viable destinations it prefers the fullest-but-not-full
one, so satellites get packed one at a time instead of spread thin.
Items moved within cooldown_days are skipped.
4. Apply¶
Apply runs in the background and returns a live-updating status page/log immediately; the moves themselves are serialized one at a time per destination physical volume (never more than one write in flight against the same disk), while different volumes proceed in parallel. Hot storage, a read source, isn't throttled.
After asking Radarr/Sonarr to relocate an item (moveFiles: true), Coldarr
watches the destination's disk usage until the transfer has actually landed
before starting the next item queued for that same volume - the move
API returns once the operation is queued, not once the bytes are on
disk, so trusting it alone isn't enough. Only one apply can run at a
time, system-wide, enforced by a crash-safe lock. Every completed move
is logged to the history file with its real completion time.
Each move is reported to Jellyfin the moment it lands - naming the vacated and new paths - so Jellyfin's rescan of those folders runs against the rest of the run instead of starting from cold once the last move finishes. Each moved item is then refreshed individually, in the background while the next move runs: Coldarr waits for Jellyfin to surface the item at its new path and forces a full metadata and image refresh on it. A moved item gets a brand-new Jellyfin item ID (Jellyfin derives IDs from the file path), and a plain library scan only fills in artwork it considers missing - so without this an item can land in the new tier with no poster at all. Just before that refresh, Coldarr puts back the date added each movie and episode had before the move, which it notes as the run starts - Jellyfin dates a moved file as newly added, so otherwise the title shows up under Recently Added again. A whole-library scan is still used as a fallback if an item can't be found at its new path. See Jellyfin.
Downloads¶
With qBittorrent connected and downloads tiers set up, there's a second, separate kind of run: finished downloads move from the download drive to seed storage once they've settled (and been imported, if an *arr app imports them), one at a time, each confirmed before the next starts, and get the seed label once they've landed. Downloads that are done seeding can be removed, if you turn that on. It's planned and applied the same way - dry run first, then Apply - from the Downloads page or a schedule. See Downloads and seeding.
A library apply and a downloads apply never run at the same time, and neither starts while Radarr, Sonarr, or qBittorrent is still moving something: moves in one can be filling the disk the other is measuring.
Confirmation¶
report and plan (or the web interface's dashboard and Plan page) are
read-only. The CLI prompts before applying unless you pass --yes; the
web interface asks for confirmation. Enabling the scheduled Run the Plan
job authorizes it to apply automatically at the configured times.
Why one destination volume at a time¶
Firing every move in a plan at once - many large simultaneous writes across several files/drives - is a very different load profile than steady, one-at-a-time movement, and can saturate a storage subsystem badly enough to make a whole host unresponsive. This isn't hypothetical; it happened during testing. The serialization is keyed by physical device, not tier name, so it also catches two differently-named tier paths that turn out to be the same disk (see Shared volumes).