For Radarr & Sonarr

Every title in the right folder, decided by your rules.

Routarr reads your libraries, enriches every title from the metadata sources you order, and decides which root folder it belongs in. You see the whole plan first, and it writes only when you say so.

Self-hosted. One container. Dry-run is on until you turn it off.

Departures · dry-run
The dry-run plan: six departures, the rule that decided each one, its destination, its gate and its status.
TitleVia ruleDestinationGateStatus
#10Japanese animation/movies/animeRADARRproposedRadarr
#20Concert films/movies/concertsRADARRproposedRadarr
#10Japanese animation/tv/animeSONARRconfirmSonarr
#30Family & kids/movies/kidsRADARRproposedRadarr
no rule matched/moviesRADARRstaysRadarr
#20Concert films/movies/concertsRADARRin placeRadarr
20 evaluated · 13 proposed · 7 in placeClick a departure to see why it goes there
  • v0.1.0
  • GPLv3 licence
  • amd64 & arm64
  • No telemetry

First matching rule wins. Exclusions can veto. Everything is explained.

In active development. The rule engine and the safety rails are stable, and the interface will keep moving.

The gap

Your Arrs already know all of this. None of it picks a folder.

Routarr is that missing decision layer: one place where the logic lives, using the folders your Arrs already have. On a webhook it decides before the download even lands, while the folder is still empty and nothing has to move.

What the payload already carriesWhere it lands

  • Akira (1988)AnimationScience Fictionja15/movies
  • Heat (1995)CrimeThrilleren15/movies
  • My Neighbor TotoroAnimationFamilyjaU/movies
  • Perfect BlueAnimationThrillerja18/movies
  • Stop Making SenseDocumentaryMusicenUconcert/movies

read by no ruleone folder, five films

Read by RoutarrWhere it lands

  • Akira (1988)AnimationScience Fictionja15#10/movies/anime
  • Heat (1995)CrimeThrilleren15/movies
  • My Neighbor TotoroAnimationFamilyjaU#30/movies/kids
  • Perfect BlueAnimationThrillerja18#10/movies/anime
  • Stop Making SenseDocumentaryMusicenUconcert#20/movies/concerts
How it works

Four steps, and you can stop after the third.

Nothing is written until an explicit action asks for it, and the global dry-run switch blocks writes at the server no matter what the interface does.

The stepReadsWrites

01

Sync

Reads films, series, tags and root folders from every Radarr and Sonarr you connect. An empty response is never treated as a mass deletion.

Radarr, Sonarr

nothing

02

Enrich

Genres, language and certification come free with the sync. Add TMDb for keywords and country: one call per title, cached, deduplicated, bounded concurrency.

TMDb, AniList, OMDb…

its own cache

03

Decide

Rules run by ascending priority, and the first match wins. Exclusions veto a rule that otherwise matched. Anything unmatched falls to your default category.

your rules

a plan you can read

04

Apply

Moves are grouped per instance and target folder, written through the Arr API, then rescanned. Every applied move can be reverted.

the plan you approved

your library

one step writes to your libraryand the global dry-run switch blocks that one at the server

The dry-run simulation screen: 20 items evaluated, 13 moves required, 7 already correct, and a table of proposed moves with source folder, target folder, matching rule, confidence and justification.
A dry-run: 13 proposed moves, each with its why, and nothing written yet.
The rule engine

28 conditions, combined your way.

Combine conditions with ALL or ANY, add explicit exclusions, order rules by priority. Genre, keyword, original language, country, certification, year, status, current folder, title, external ids and age, plus the signals only your Arr has: tags, Sonarr's series type, size on disk and season count.

Japanese animation

Priority 10Films & series

routes to anime

Conditions

AllAny

original_languageis one ofja

genre_containscontainsAnimation

Except when

certification_inis one ofGUTV-Y

Exclusions are why this works. "Japanese animation" would swallow My Neighbor Totoro. The deliberate exclusion of G-rated certifications hands it to kids instead, and the interface says so.

Rules export and import as JSON, so they can be versioned outside the application, and a preview shows exactly what a change would do before it is saved.

Explainability

Every decision shows its work, including the rules that lost.

A classification you cannot audit is one you cannot trust. Routarr records, condition by condition, what was expected and what was observed, for the winning rule and for every rule that did not win.

Akira (1988) · every rule that looked at it

#10

Japanese animation

won

met — original_languageexpected ja
observed ja

met — genre_containsexpected Animation
observed Animation, Science Fiction

met — certification_inexpected not G, U, TV-Y
observed 15

routed to anime, 70% confidence
#20

Concert films

did not match

not met — genre_containsexpected Documentary, Music
observed Animation, Science Fiction

one condition short
#30

Family & kids

did not match

not met — certification_inexpected G, U, TV-Y
observed 15

met — genre_containsexpected Animation
observed Animation, Science Fiction

matched on genre, failed on certification

The engine itself never writes prose: it emits keys and parameters, and the text is rendered in your language at display time. The justifications are translated in all 26 languages, not frozen in English at the moment they were computed.

The explanation panel for Akira: proposed category anime at 70% confidence, its TMDb metadata, and each rule with its conditions marked matched or not matched.
Safety

Nothing is written until six gates agree.

Routarr moves files on disk. Every one of these is on by default, they run in this order, and the first one to object stops the rest. The first alone is enough to make the whole application read-only.

  1. 01

    Global dry-run

    refuses

    On from the first launch. While it is on, nothing is written, whatever you click and whatever automation is enabled.

  2. 02

    Batch cap

    refuses

    A hard ceiling on how many items one apply can touch. Reclassifying a whole library is a separate, deliberate action.

  3. 03

    Reachability

    asks

    A destination that did not answer the last time anyone looked. A NAS that has spun down cannot be told from a dead disk, so this one asks rather than refuses.

  4. 04

    Capacity

    asks

    The plan weighed against the free space the sync already recorded, counting only the bytes that cross a filesystem.

  5. 05

    Confirmation threshold

    asks

    Past a number you choose, a second explicit confirmation is required, recognised by a stable error code rather than by matching English text.

  6. 06

    Revalidation at apply time

    refuses

    Each decision is checked again against the current rules and library. A proposal the rules no longer justify is refused, not executed.

refusesstops the applyasksraises a question under its own name, which you answer

Automatic application exists, and it is off by default. When enabled it touches only titles with no files on disk yet, the case where changing the folder is a metadata write and nothing moves. Anything already downloaded stays in the human queue.

Root folders

Your folders, your names, nothing invented.

Routarr never creates a library layout. It reads the root folders Radarr and Sonarr declare, and where you want a destination they do not list you type it here and it is marked as such. Either way you attach it to a category you named, and a declared folder takes its free space and reachability from the synced folder it sits under.

PathInstanceOriginFree spaceCategory

/moviesRadarrsynced2.4 TiBstandard

/movies/animeRadarrdeclared2.4 TiBanime

/movies/kidsRadarrdeclared2.4 TiBkids

/movies/concertsRadarrdeclared2.4 TiBconcerts

/tvSonarrsynced6.1 TiBstandard

/tv/animeSonarrsynced6.1 TiBanime

/mnt/archiveSonarrsyncedunreachableunmapped

seven destinations across two instancesthree declared here, four read from the Arrs

Free space and reachability come straight from the Arr. Two folders claiming the same category on one instance is refused rather than silently resolved, and a category no folder provides is reported before it can quietly skip your media.

The root folders screen: seven folders across Radarr and Sonarr, each showing its path, free space, accessible state and the category it is mapped to.
Seven folders across two instances, each mapped to a category.
Features

Everything else it does.

One dashboard over every connected instance: what is tracked, what is enriched, what is waiting for you.

The dashboard: 14 movies, 6 series, 4 active rules, 13 pending decisions, full TMDb metadata coverage, and both Radarr and Sonarr instances reporting as connected.

Specificationv0.1.0

Instances

any numberMulti-instance

Any number of Radarr and Sonarr servers, each with its own sync interval, folders and mappings.

Ingest

a secret URL per instanceReal-time webhooks

A per-instance secret URL. New additions are synced, enriched and evaluated immediately instead of at the next poll.

Overrides

outranks every ruleOverride and revert

Pin a title to a category and the engine stops arguing. An override outranks every rule, and any applied move can be undone from the history.

Observability

/metrics, same API keyPrometheus metrics

Library size, decisions per category and status, instance health, and the state of the guardrails, all behind the same API key.

Notifications

one generic JSON webhookOutbound notifications

One generic JSON webhook for Discord, Gotify, ntfy or Apprise, fired on a state change and never on a loop.

Portability

AES-256-GCM at restPortable config, sealed keys

Rules, categories, mappings and overrides export as JSON and travel between installations. Arr API keys are AES-256-GCM-encrypted and never leave.

Install

Up in one docker compose up.

One image carries the backend and the interface, for amd64 and arm64: a Synology or QNAP NAS, Unraid, TrueNAS, a Raspberry Pi or a plain VPS. Save the compose file, start it, and the first run needs no configuration file.

  1. 01

    mkdir -p data && sudo chown 1000:1000 data

    The container runs as uid 1000 and drops every capability, so the volume has to belong to it.

  2. 02

    docker compose up -d

    One image, backend and interface together. Nothing else to install.

  3. 03

    docker exec routarr cat /data/routarr.api_key

    The key is generated on first start and written beside the database. The log prints it once, the file keeps it.

  4. 04

    http://localhost:9876

    Global dry-run is already on. Nothing can be written until you turn it off.

docker-compose.yml
services:
  routarr:
    image: ghcr.io/routarr/routarr:latest
    container_name: routarr
    ports:
      - "9876:9876"
    volumes:
      - ./data:/data
    restart: unless-stopped
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

Back up routarr.db and routarr.key together. Without the key, the stored Arr credentials cannot be recovered.

Then: connect your Arrs, map your folders to categories, write a rule and preview it, run a dry-run, and turn the global dry-run off only when you agree with the plan.

Questions

Before you install it.

Most of the answers below are no, which is the most useful thing this section has to say.

The worryThe answerWhat guarantees it

Does it move my files around?

only if you ask twice

Global dry-run, on by default

Global dry-run is on from the first launch, the option to move files on disk is unchecked by default, and applying past a threshold needs an explicit confirmation. Without the disk-move option, Routarr only re-points the library entry and leaves the files where they are.

Will it rename my folders?

no

The root folder, not the name

A move changes the root folder above your title and keeps the folder name exactly as it is, so a library pointed at it by Plex or Jellyfin keeps working.

Do I need a TMDb key?

no

The Arrs are a source of their own

Radarr and Sonarr are a metadata source of their own. They already report genres, original language and certification, and Routarr reads them from the same response it syncs the library with. For keywords and origin country, AniList needs no key either. TMDb, OMDb and TheTVDB are there if you want them. Sources are an ordered list, so you decide which one wins when they disagree.

Does it replace Radarr or Sonarr?

no

Two fields, through their own API

It deliberately does not try. Routarr never manages downloads, indexers or the library itself. It writes exactly two things through the Arr API, the root folder and the path, then asks for a rescan.

Does it phone home?

never

No outbound request nobody asked for

No telemetry, no analytics, no update check. The only outbound calls go to your Radarr and Sonarr instances, to the metadata sources you enable (TMDb, AniList, Jikan, OMDb, TheTVDB), to your OpenID Connect provider if you use one, and to a notification webhook if you set one.

Can I put it behind a reverse proxy?

yes

One environment variable

Including under a sub-path. Set ROUTARR_BASE_PATH=/routarr and the API, the interface and the webhook URLs all move with it, with no image rebuild.

What does it run on?

a Pi is enough

One image, one file

One Docker image for amd64 and arm64, so a Pi or an N100 is enough. Storage is a single SQLite file. There is no database service to run.

Stop choosing the folder by hand.

Read the plan, keep what you agree with, and let the rules do the rest.

What you have just been shown

It cannot write until you let it

Global dry-run is on from the first launch, and five more gates stand after it.

Every decision shows its work

What each condition expected, what it observed, and the rules that did not win.

One image, one file, one port

amd64 and arm64, a single SQLite file, no database service to run.