Skip to content
Jared edited this page Mar 18, 2026 · 7 revisions

Welcome to the Sortarr wiki!

Sortarr

Version

0.8.6 Release Notes

0.8.6 is the release focused on resolving the current GitHub CSRF and auth issue set.

  • Fixes setup/save CSRF failures across direct HTTP, LAN proxy, and trusted HTTPS proxy deployments by making cookie security request-aware and by preserving trusted X-Forwarded-* headers through Waitress.
  • Adds a simple Sonarr-style authentication choice: Basic or External. Direct installs and transparent reverse proxies keep Basic; reverse proxies that already handle login can now opt into External.
  • Preserves submitted setup values after failed save/test attempts and adds clearer diagnostics when cookie transport or proxy trust would break the next POST.
  • If you are on 0.8.5.1 and seeing token-missing-cookie or CSRF origin mismatch on every Save, upgrade to 0.8.6 first before changing proxy settings further.

Features

  • Sonarr series size stats (total includes specials; averages exclude specials)
  • Expand Sonarr series rows to view season rollups and episode lists
  • Radarr movie size stats (file size and GiB per hour)
  • Bitrate metric with estimated fallback indicator
  • Filter builder dropdowns with active filter bubbles (quick chips optional)
  • Filter builder Category dropdown is alphabetized and searchable (desktop custom dropdown)
  • Optional Arr metadata columns (status, monitored, quality profile, tags, custom formats, release group, many more)
  • Sonarr/Radarr health badges to surface instance warnings
  • Sonarr series metadata and wanted columns (series type, original language, genres, last aired, missing/cutoff counts)
  • Sonarr chips for missing, cutoff unmet, recently grabbed, scene numbering, and airing
  • Fixed-width Title column with CSS ellipsis to keep large tables stable
  • Reset UI now clears in-memory and persisted filter/chip/view state in one action
  • Header/layout and table-wrap sizing work is batched and memoized to reduce first-paint layout thrash
  • Startup non-critical UI bindings and status polling are deferred/coalesced to reduce cold-load churn
  • Fullscreen Data Table button to focus the table on small screens
  • CSV export for Sonarr and Radarr (playback columns appear only when a playback provider is configured)
  • Date added column for Sonarr and Radarr views
  • Compressed CSV API responses for large payloads
  • Multiple Sonarr/Radarr instances with optional friendly names
  • Path mapping support for Docker volume paths (map container paths to host paths)
  • Simple authentication boundary choice: Basic or External
  • Request-aware CSRF diagnostics and reverse-proxy troubleshooting
  • Optional playback stats from Tautulli, Jellystat, and/or Plex with a selectable preferred history source
  • Playback match status orbs beside titles to flag potential mismatches (only with a playback provider configured)
  • Shows/Movies top-level tabs with optional per-tab Plex library scoping (multi-select)
  • Plex Insights drawer with hubs (section filter), match health summary, activities, and butler tasks (when Plex is configured)
  • Audio/subtitle language columns with filters and quick chips
  • Exact FPS and BPPF for Radarr movie rows, plus exact FPS / BPPF in Sonarr episode expansions when Arr file metadata reports frame rate

Deployment (Docker)

Quick start

Deploy Sortarr using Docker Compose for the easiest setup.

  1. Prepare docker-compose.yaml: Clone the repository or download the docker-compose.yaml file.
  2. Start the container:
    docker compose up -d

Image Sources:

  • Default (GHCR): ghcr.io/jaredharper1/sortarr:latest (Recommended for release builds).
  • Docker Hub: docker.io/jaredharper1/sortarr:latest.
  • Architectures: Supported for linux/amd64 and linux/arm64/v8 (Apple Silicon compatible).

ACCESS AND SETUP

  • URL: Open http://<host>:9595 in your browser.
  • Initialization: The first visit automatically redirects to /setup, where you can configure your Sonarr/Radarr instances and choose who handles login: Basic or External.

Setup Page

  • Configuration Persistence: Ensure the ./data directory is mounted as a persistent volume (e.g., - ./data:/data). Sortarr writes configuration to /data/Sortarr.env by default (controlled by SORTARR_CONFIG_PATH).

Note: Initial library loading may take several minutes depending on library size and playback provider configuration; subsequent loads utilize the persistent cache.

Deployment (Unraid)

An Unraid template is provided at docs/unraid-template.xml to simplify deployment.

Install via template

  1. Copy Template: Place the template file into Unraid’s user templates directory:
    • Source: docs/unraid-template.xml
    • Destination: /boot/config/plugins/dockerMan/templates-user/sortarr.xml
  2. Add Container: In the Unraid WebUI, go to DockerAdd Container.
  3. Select Template: Choose sortarr from the Template dropdown.
  4. Configure Fields:
    • WebUI Port: Default is 9595 (maps to internal 8787).
    • Appdata Path: Recommended /mnt/user/appdata/sortarr (mapped to /config).
  5. Advanced Settings (Optional):
    • PUID/PGID: Defaults to 99 and 100. Adjust if your appdata requires specific permissions.
    • ENV_FILE_PATH: Defaults to /config/Sortarr.env.
    • Network & CSRF: Set SORTARR_PROXY_MODE (Direct, Single proxy, Two proxies, Custom) to match your reverse-proxy chain.
    • Waitress trusted proxy: Set SORTARR_WAITRESS_TRUSTED_PROXY to the immediate upstream proxy IP/host for Waitress trusted-proxy handling.
    • Path-prefix proxy hops: Use SORTARR_PROXY_MODE=custom with SORTARR_PROXY_HOPS_PREFIX=1 only if you actually publish Sortarr under a path prefix.
    • Session secret key: Use Setup to generate/persist a permanent key for stable sessions and CSRF across restarts/replicas.
  6. Apply: Start the container and open the WebUI.

Support: Use GitHub Issues for Unraid-specific support until a community thread is established.

The first load after startup can take a while for large libraries (especially with a playback provider configured); later loads are cached and faster.

Deployment (Windows EXE)

Sortarr can be run as a standalone Windows executable. This is ideal for users who prefer not to use Docker.

Execution

  1. Download: Obtain the Sortarr.exe (PyInstaller --onefile build).
  2. Run: Double-click the EXE to start the application. A terminal window will open, and the web server will start on the default port (8787).
  3. Setup: Open http://localhost:8787 in your browser.

Configuration Persistence

Since single-file executables extract their contents to a temporary directory on each launch, Sortarr uses an external .env file to persist your settings:

  • Automatic Initialization: On the first launch, Sortarr creates a blank .env file in the same directory as the EXE.
  • Setup Page: When you save settings on the /setup page, they are written to this .env file.
  • UI Hint: The exact path to the active configuration file is displayed at the bottom of the Setup page for easy reference.

Path Overrides

If you need to store the configuration elsewhere, you can set the following environment variables before running the EXE:

  • SORTARR_CONFIG_PATH (Preferred): The absolute path to your configuration file (e.g., C:\Sortarr\myconfig.env).
  • ENV_FILE_PATH: An alternative variable for the configuration path.

Note: Relative paths are resolved against the directory containing the EXE.

Deployment (Python / Source)

For developers or users who prefer running directly from the source code.

Prerequisites

  • Python 3.10+
  • pip (Python package installer)

Setup and Execution

  1. Install Dependencies:
    pip install -r requirements.txt
  2. Run Application:
    python app.py
  3. Access: Open http://localhost:8787 in your browser.

Local Configuration

  • Environment: You can set variables like PORT (default 8787) or HOST (default 0.0.0.0) in your shell before running.
  • Persistence: On first launch, Sortarr creates a .env file in the project root. You can also use SORTARR_CONFIG_PATH to point to a specific file.
  • Production Server: By default, Sortarr uses waitress to serve the application. Set SORTARR_USE_WAITRESS=0 only for local development/debugging with the Flask built-in server.

Internationalization

Sortarr supports multiple languages (currently English and German).

Language Selection

You can switch the interface language using the links in the top-right corner of the Setup page:

  • Deutsch: Switches the UI to German.
  • English: Switches the UI to English (default).

Persistence

Sortarr remembers your language choice using a combination of methods:

  1. URL Parameter: Adding ?lang=de or ?lang=en to any URL will force that language for the current request.
  2. Session: Your choice is saved in your browser's session cookie, persisting as long as your session is active.
  3. Browser Preference: If no manual choice is made, Sortarr will attempt to match your browser's preferred language settings.

Note: Translation files are located in the translations/ directory.

Configuration Migration and Defaults

Sortarr automatically manages configuration transitions and cache integrity during version upgrades.

Automatic Migrations

On the first run after an update, Sortarr performs the following:

  • Version Detection: It compares the current version against the last recorded version in Sortarr.startup.json.
  • Environment Cleanup: If a version change is detected, legacy default values for variables like TAUTULLI_TIMEOUT_SECONDS or TAUTULLI_METADATA_LOOKUP_LIMIT are automatically removed from your .env file. This allows Sortarr to apply updated defaults optimized for the new version.
  • Mandatory Overrides: Certain settings, such as Tautulli lookup limits, are enforced to ensure accuracy (e.g., unlimited lookups by default).
  • Identifier Generation: If Plex is configured but missing a PLEX_CLIENT_ID, a unique identifier is automatically generated and saved.
  • 0.8.3 security gate: Upgrades from 0.8.2.1 and earlier are routed through a one-time Setup completion flow so new secret/session defaults are persisted safely.

0.8.3 Setup Enforcement (Security)

  • Persistent session-secret references are the required steady-state model after setup completes.
  • First bootstrap may use a temporary ephemeral session secret before the first successful Setup save.
  • If the Session secret key field is blank, Setup auto-generates and persists a session-secret reference before save.
  • Configured startup aborts if the persistent session secret cannot be resolved and unsafe recovery is not enabled.
  • The one-time upgrade gate clears after successful Setup save.
  • Advanced recovery override: SORTARR_ALLOW_UNSAFE_EPHEMERAL_RECOVERY=1 (recovery only; it is bounded and should be disabled after use).

Cache Invalidation

When an upgrade is detected, Sortarr triggers a mandatory cache wipe. This includes:

  • Sonarr and Radarr library caches.
  • Tautulli, Jellystat, and Plex metadata/history caches.
  • Background refresh locks and markers.

This ensures that any data format changes or new metrics in the update are correctly populated from your media providers without interference from stale data.

Sonarr and Radarr Instances

Sortarr supports connecting to multiple Sonarr and Radarr instances simultaneously. While the Setup UI provides fields for up to three instances of each, the backend can support more if configured manually in your .env file.

Primary Instance (Required)

At minimum, one Sonarr or Radarr instance must be configured.

  • SONARR_URL / RADARR_URL: The base URL of your instance.
  • SONARR_API_KEY / RADARR_API_KEY: The API key (read-only is recommended).

Optional per-Instance Fields

  • NAME: A friendly name (e.g., "4K", "Anime"). If multiple instances are configured, names are required to distinguish them in the UI.
  • PATH_MAP: Maps container paths to host paths for UI display. Format: src:dst. Multiple entries can be separated by |, ,, or ;.
    • Example: /downloads:/mnt/media/downloads | /movies:/mnt/user/movies

Split-Network Configuration (Advanced)

For setups where Sortarr and your browser access Arr instances via different routes (e.g., Kubernetes or VPNs), you can separate the internal API URL from the external UI URL.

  • *_URL_API: Internal URL used by Sortarr for HTTP requests. Falls back to *_URL.
  • *_URL_EXTERNAL: External URL used for clickable links in the Sortarr UI. Falls back to *_URL_API.

Example:

SONARR_URL_API=http://sonarr.local:8989
SONARR_URL_EXTERNAL=https://sonarr.mydomain.com

Additional Instances

Configure additional instances using a numbered suffix (starting from _2).

  • Sonarr: SONARR_URL_2, SONARR_API_KEY_2, SONARR_NAME_2, etc.
  • Radarr: RADARR_URL_2, RADARR_API_KEY_2, RADARR_NAME_2, etc.

Tautulli (optional)

Playback stats from Tautulli can be integrated to show watch history and bitrate metrics.

  • TAUTULLI_URL: The base URL of your Tautulli instance.
  • TAUTULLI_API_KEY: Your Tautulli API key.

Jellystat (optional)

Playback stats from Jellystat can be integrated as an alternative or additional history source.

  • JELLYSTAT_URL: The base URL of your Jellystat instance.
  • JELLYSTAT_API_KEY: Your Jellystat API key.
  • JELLYSTAT_LIBRARY_IDS_SONARR: Optional. Comma-separated list of Jellystat library IDs to include for Sonarr matching. If blank, all libraries containing series are used.
  • JELLYSTAT_LIBRARY_IDS_RADARR: Optional. Comma-separated list of Jellystat library IDs to include for Radarr matching. If blank, all libraries containing movies are used.

Plex (optional)

Plex can be used as a media source, a history source, or for provider insights.

  • PLEX_URL: The base URL of your Plex server.
  • PLEX_TOKEN: Your Plex authentication token.
  • PLEX_SECTION_FILTERS: Comma-separated list of section IDs to include.

Runtime Overrides

Global settings that control the server behavior, performance, and security. These can be set as environment variables or in your .env file.

Startup Reference (Canonical)

This is the canonical startup reference for Sortarr runtime configuration. Use Sortarr.minimal.env.example first, then add advanced/compatibility variables only when required.

Supported surface (minimal)
  • SORTARR_SECRET_KEY_FILE or SORTARR_SECRET_KEY_CRED_TARGET
  • One media path:
  • Arr: SONARR_URL + SONARR_API_KEY* and/or RADARR_URL + RADARR_API_KEY*
  • Plex: PLEX_URL + PLEX_TOKEN*
  • SORTARR_PROXY_MODE (direct|single|double|custom)
  • SORTARR_WAITRESS_TRUSTED_PROXY (recommended for proxied deployments; required for SORTARR_AUTH_METHOD=external)
  • Authentication boundary Basic: SORTARR_AUTH_METHOD=basic plus BASIC_AUTH_USER + (BASIC_AUTH_PASS_FILE or BASIC_AUTH_PASS_CRED_TARGET)
  • Authentication boundary External: SORTARR_AUTH_METHOD=external plus SORTARR_UPSTREAM_AUTH_HEADER
  • Optional CSRF escape hatch: SORTARR_CSRF_TRUSTED_ORIGINS (exact origins only)

Everything outside this set is advanced, legacy compatibility, or internal/enforced.

Startup commands and args
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
startup command python app.py (local), container entrypoint (Docker), Sortarr.exe (Windows EXE) n/a Docker, EXE, local Starts web app process none
CLI args none (Sortarr runtime is env-driven) n/a Docker, EXE, local Runtime behavior is configured via env vars / .env none
Startup variable classification (exhaustive)

Classification labels: core, advanced, legacy, internal/enforced.

core
Variable/arg name
BASIC_AUTH_PASS
BASIC_AUTH_USER
ENV_FILE_PATH
HISTORY_SOURCE_PREFERENCE
HOST
MEDIA_SOURCE_PREFERENCE
PLEX_TOKEN
PLEX_URL
PORT
RADARR_API_KEY
RADARR_API_KEY_2
RADARR_API_KEY_2_CRED_TARGET
RADARR_API_KEY_2_FILE
RADARR_API_KEY_3
RADARR_API_KEY_3_CRED_TARGET
RADARR_API_KEY_3_FILE
RADARR_API_KEY_CRED_TARGET
RADARR_API_KEY_FILE
RADARR_URL
RADARR_URL_2
RADARR_URL_3
RADARR_URL_API
RADARR_URL_API_2
RADARR_URL_API_3
RADARR_URL_EXTERNAL
RADARR_URL_EXTERNAL_2
RADARR_URL_EXTERNAL_3
SORTARR_AUTH_METHOD
SONARR_API_KEY
SONARR_API_KEY_2
SONARR_API_KEY_2_CRED_TARGET
SONARR_API_KEY_2_FILE
SONARR_API_KEY_3
SONARR_API_KEY_3_CRED_TARGET
SONARR_API_KEY_3_FILE
SONARR_API_KEY_CRED_TARGET
SONARR_API_KEY_FILE
SONARR_URL
SONARR_URL_2
SONARR_URL_3
SONARR_URL_API
SONARR_URL_API_2
SONARR_URL_API_3
SONARR_URL_EXTERNAL
SONARR_URL_EXTERNAL_2
SONARR_URL_EXTERNAL_3
SORTARR_CONFIG_PATH
SORTARR_CSRF_TOKEN_TTL_SECONDS
SORTARR_CSRF_TRUSTED_ORIGINS
SORTARR_LOG_LEVEL
SORTARR_PROXY_HOPS
SORTARR_PROXY_MODE
SORTARR_SECRET_KEY
SORTARR_SECRET_KEY_CRED_TARGET
SORTARR_SECRET_KEY_FILE
SORTARR_SESSION_COOKIE_SAMESITE
SORTARR_SESSION_COOKIE_SECURE
SORTARR_UPSTREAM_AUTH_HEADER
SORTARR_USE_WAITRESS
SORTARR_WAITRESS_TRUSTED_PROXY
WAITRESS_THREADS
advanced
Variable/arg name
ARR_BACKOFF_BASE_SECONDS
ARR_BACKOFF_MAX_RETRIES
ARR_CIRCUIT_FAIL_THRESHOLD
ARR_CIRCUIT_OPEN_SECONDS
ARR_FILENAME_CACHE_MAX_ENTRIES
ARR_FILENAME_CACHE_TTL_SECONDS
ARR_HISTORY_CACHE_TTL_SECONDS
BASIC_AUTH_PASS_CRED_TARGET
BASIC_AUTH_PASS_FILE
CACHE_SECONDS
JELLYSTAT_API_KEY
JELLYSTAT_API_KEY_CRED_TARGET
JELLYSTAT_API_KEY_FILE
JELLYSTAT_CACHE_PATH
JELLYSTAT_HISTORY_PAGE_SIZE
JELLYSTAT_LIBRARY_IDS_RADARR
JELLYSTAT_LIBRARY_IDS_SONARR
JELLYSTAT_REFRESH_STALE_SECONDS
JELLYSTAT_STATS_PAGE_SIZE
JELLYSTAT_TIMEOUT_SECONDS
JELLYSTAT_URL
PLEX_CACHE_PATH
PLEX_CLIENT_ID
PLEX_HISTORY_LAST_VIEWED_AT
PLEX_HISTORY_PAGE_SIZE
PLEX_REFRESH_STALE_SECONDS
PLEX_SECTION_FILTERS
PLEX_TOKEN_CRED_TARGET
PLEX_TOKEN_FILE
RADARR_CACHE_PATH
RADARR_INSTANCE_WORKERS
RADARR_NAME
RADARR_NAME_2
RADARR_NAME_3
RADARR_PATH_MAP
RADARR_PATH_MAP_2
RADARR_PATH_MAP_3
RADARR_TIMEOUT_SECONDS
RADARR_WANTED_WORKERS
SONARR_CACHE_PATH
SONARR_EPISODEFILE_CACHE_SECONDS
SONARR_EPISODEFILE_DELAY_SECONDS
SONARR_EPISODEFILE_MODE
SONARR_EPISODEFILE_WORKERS
SONARR_NAME
SONARR_NAME_2
SONARR_NAME_3
SONARR_PATH_MAP
SONARR_PATH_MAP_2
SONARR_PATH_MAP_3
SONARR_TIMEOUT_SECONDS
SORTARR_ALLOW_PLAINTEXT_SECRETS
SORTARR_ALLOW_UNSAFE_EPHEMERAL_RECOVERY
SORTARR_DEFER_WANTED
SORTARR_PROXY_HOPS_FOR
SORTARR_PROXY_HOPS_HOST
SORTARR_PROXY_HOPS_PORT
SORTARR_PROXY_HOPS_PREFIX
SORTARR_PROXY_HOPS_PROTO
SORTARR_PUBLIC_HOST
SORTARR_PUBLIC_ORIGIN
SORTARR_PUBLIC_URL
SORTARR_RADARR_LITE_FIRST
SORTARR_STRICT_INSTANCE_ERRORS
SORTARR_WINDOWS_CREDSTORE
TAUTULLI_API_KEY
TAUTULLI_API_KEY_CRED_TARGET
TAUTULLI_API_KEY_FILE
TAUTULLI_FETCH_SECONDS
TAUTULLI_METADATA_CACHE
TAUTULLI_METADATA_SAVE_EVERY
TAUTULLI_METADATA_WORKERS
TAUTULLI_REFRESH_STALE_SECONDS
TAUTULLI_TIMEOUT_SECONDS
TAUTULLI_URL
legacy
Variable/arg name
SECRET_KEY
SECRET_KEY_FILE
internal/enforced
Variable/arg name
TAUTULLI_METADATA_LOOKUP_LIMIT
TAUTULLI_METADATA_LOOKUP_SECONDS
Core server/runtime
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
PORT integer 1..65535 8787 Docker, EXE, local Bind port for HTTP server Do not expose directly to internet unless intended
HOST host/interface string 0.0.0.0 Docker, EXE, local Bind interface Use restricted bind in hardened environments
SORTARR_USE_WAITRESS 0/1, true/false, yes/no, on/off 1 Docker, EXE, local 1 uses Waitress; 0 uses Flask dev server Flask dev server is not production-safe
WAITRESS_THREADS integer >=1 4 Docker, EXE, local Waitress worker thread count none
SORTARR_LOG_LEVEL DEBUG, INFO, WARNING, ERROR, CRITICAL INFO Docker, EXE, local Logging verbosity Avoid DEBUG in shared logs (more metadata exposure risk)
ENV_FILE_PATH filesystem path app default path Docker, EXE, local Path to env file (load/save) Treat as sensitive config
SORTARR_CONFIG_PATH filesystem path unset Docker, EXE, local Overrides active env file path Treat as sensitive config
SORTARR_WINDOWS_CREDSTORE 0/1, bool-like platform dependent EXE/local Windows Enables Windows Credential Manager resolution path Prefer enabled for secret hygiene
Proxy / CSRF / session security
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
SORTARR_PROXY_MODE direct, single, double, custom auto-derived from hops (0->direct, 1->single, 2->double) Docker, EXE, local Primary proxy trust mode selection Prefer this as the primary proxy setting
SORTARR_PROXY_HOPS integer >=0 0 Docker, EXE, local Base trust count for X-Forwarded-For Enable only when app is actually behind trusted proxy
SORTARR_PROXY_HOPS_FOR integer >=0 SORTARR_PROXY_HOPS Docker, EXE, local Explicit trust for X-Forwarded-For Same as above
SORTARR_PROXY_HOPS_HOST integer >=0 1 when proxied else 0 Docker, EXE, local Trust count for X-Forwarded-Host Misconfiguration can cause origin mismatch
SORTARR_PROXY_HOPS_PROTO integer >=0 1 when proxied else 0 Docker, EXE, local Trust count for X-Forwarded-Proto Misconfiguration can cause scheme drift
SORTARR_PROXY_HOPS_PORT integer >=0 1 when proxied else 0 Docker, EXE, local Trust count for X-Forwarded-Port none
SORTARR_PROXY_HOPS_PREFIX integer >=0 0 Docker, EXE, local Trust count for X-Forwarded-Prefix Enable only for explicit path-prefix proxy setups
SORTARR_WAITRESS_TRUSTED_PROXY IP, hostname, or * empty (* fallback when proxied) Docker, EXE, local Immediate upstream proxy trusted by Waitress for forwarded-header preservation Prefer a specific immediate proxy over wildcard trust
SORTARR_CSRF_TOKEN_TTL_SECONDS integer >=60 7200 Docker, EXE, local Session-bound CSRF token TTL Short TTL can cause frequent token expiry
SORTARR_CSRF_TRUSTED_ORIGINS comma-separated exact origins (scheme://host[:port]) empty Docker, EXE, local Optional token-gated origin/referer fallback allow-list Exact match only; keep minimal
SORTARR_SESSION_COOKIE_SAMESITE Lax, Strict, None Lax Docker, EXE, local Session cookie SameSite policy None typically requires secure cookie
SORTARR_SESSION_COOKIE_SECURE 0/1, bool-like, or unset request-aware auto Docker, EXE, local Overrides request-aware cookie security when explicitly set; unset follows effective scheme/HTTPS hints Use 0 for plain HTTP deployments; use 1 only when you are sure browsers always reach Sortarr over HTTPS
SORTARR_PUBLIC_HOST / SORTARR_PUBLIC_URL / SORTARR_PUBLIC_ORIGIN exact public URL/origin value empty Docker, EXE, local Optional HTTPS hint for cookie security when the public deployment is HTTPS but proxy trust is incomplete Only use https://... values that match your real public deployment; remove stale HTTPS values on plain HTTP installs
Auth and secret-key posture
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
SORTARR_AUTH_METHOD basic, external basic Docker, EXE, local Selects whether Sortarr handles login itself or trusts a verified upstream identity header external is opt-in and should only be used behind a trusted reverse proxy
SORTARR_UPSTREAM_AUTH_HEADER HTTP header name X-Forwarded-User Docker, EXE, local Header used for authenticated identity in external mode Only trusted from the configured immediate proxy; direct spoofed headers are rejected
BASIC_AUTH_USER string empty Docker, EXE, local Enables Sortarr-managed Basic Auth when SORTARR_AUTH_METHOD=basic and a password is configured Use with TLS only
BASIC_AUTH_PASS string/secret ref empty Docker, EXE, local Basic auth password Prefer _FILE / _CRED_TARGET / wincred:
SORTARR_SECRET_KEY string/secret ref compatibility input empty Docker, EXE, local Session-secret migration or compatibility input Plaintext is migration-only; prefer _FILE / _CRED_TARGET / wincred:
SORTARR_SECRET_KEY_FILE filesystem path empty Docker, EXE, local Preferred file-based source for session key Recommended for Docker/Unraid
SORTARR_SECRET_KEY_CRED_TARGET Windows cred target empty EXE/local Windows Explicit Windows credential target for session key Preferred on Windows
SECRET_KEY string/secret ref compatibility input (legacy alias) empty Docker, EXE, local Backward-compatible session-secret input Legacy only; plaintext is migration-only
SECRET_KEY_FILE filesystem path (legacy alias) empty Docker, EXE, local Backward-compatible alias for key file Legacy only; prefer SORTARR_SECRET_KEY_FILE
SORTARR_ALLOW_UNSAFE_EPHEMERAL_RECOVERY 0/1, bool-like 0 Docker, EXE, local Temporary lockout-recovery override Recovery-only; disable after use
SORTARR_ALLOW_PLAINTEXT_SECRETS 0/1, bool-like 0 Docker, EXE, local Legacy migration compatibility flag Persisted back to 0; not a supported steady-state runtime mode
Secret precedence fixed order n/a Docker, EXE, local *_FILE -> *_CRED_TARGET / wincred: with legacy SECRET_KEY* aliases for compatibility Keep only one active source when possible
Media/history provider selection
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
MEDIA_SOURCE_PREFERENCE arr, plex, auto arr Docker, EXE, local Selects effective media source none
HISTORY_SOURCE_PREFERENCE tautulli, jellystat, plex, auto auto Docker, EXE, local Selects effective playback/history source none
SORTARR_RADARR_LITE_FIRST 0/1, bool-like 1 Docker, EXE, local Prefers Radarr-lite first pass behavior none
SORTARR_STRICT_INSTANCE_ERRORS 0/1, bool-like 0 Docker, EXE, local Raises on per-instance fetch failure instead of warning/partial results none
SORTARR_DEFER_WANTED 0/1, bool-like 1 Docker, EXE, local Defers wanted/missing overlay on cold loads none
Sonarr/Radarr instance families (all startup-loadable)
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
SONARR_URL, SONARR_URL_2, SONARR_URL_3 URL empty Docker, EXE, local Sonarr instance base URLs none
SONARR_URL_API, _2, _3 URL empty Docker, EXE, local Internal/API Sonarr URLs (split-network mode) none
SONARR_URL_EXTERNAL, _2, _3 URL empty Docker, EXE, local External/UI Sonarr URLs for links none
SONARR_NAME, _2, _3 string empty Docker, EXE, local Instance display labels none
SONARR_PATH_MAP, _2, _3 path map string (src:dst, separators ` , ,, ;`) empty Docker, EXE, local Container->host path mapping for UI path display
SONARR_API_KEY, _2, _3 secret / wincred:<target> empty Docker, EXE, local Sonarr API auth Sensitive; prefer companion _FILE / _CRED_TARGET
RADARR_URL, RADARR_URL_2, RADARR_URL_3 URL empty Docker, EXE, local Radarr instance base URLs none
RADARR_URL_API, _2, _3 URL empty Docker, EXE, local Internal/API Radarr URLs (split-network mode) none
RADARR_URL_EXTERNAL, _2, _3 URL empty Docker, EXE, local External/UI Radarr URLs for links none
RADARR_NAME, _2, _3 string empty Docker, EXE, local Instance display labels none
RADARR_PATH_MAP, _2, _3 path map string (src:dst, separators ` , ,, ;`) empty Docker, EXE, local Container->host path mapping for UI path display
RADARR_API_KEY, _2, _3 secret / wincred:<target> empty Docker, EXE, local Radarr API auth Sensitive; prefer companion _FILE / _CRED_TARGET
Playback-provider connection and cache vars
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
TAUTULLI_URL URL empty Docker, EXE, local Tautulli endpoint none
TAUTULLI_API_KEY secret / wincred:<target> empty Docker, EXE, local Tautulli API auth Sensitive; prefer _FILE / _CRED_TARGET
JELLYSTAT_URL URL empty Docker, EXE, local Jellystat endpoint none
JELLYSTAT_API_KEY secret / wincred:<target> empty Docker, EXE, local Jellystat API auth Sensitive; prefer _FILE / _CRED_TARGET
JELLYSTAT_LIBRARY_IDS_SONARR comma-separated IDs empty Docker, EXE, local Sonarr-side Jellystat library filter none
JELLYSTAT_LIBRARY_IDS_RADARR comma-separated IDs empty Docker, EXE, local Radarr-side Jellystat library filter none
PLEX_URL URL empty Docker, EXE, local Plex endpoint none
PLEX_TOKEN secret / wincred:<target> empty Docker, EXE, local Plex auth token Sensitive; prefer _FILE / _CRED_TARGET
PLEX_CLIENT_ID string empty (auto-generated in some setup flows) Docker, EXE, local Plex client identifier for requests Avoid sharing publicly
PLEX_SECTION_FILTERS comma-separated section IDs empty Docker, EXE, local Limits Plex libraries scanned none
TAUTULLI_METADATA_CACHE filesystem path app default Docker, EXE, local Path for Tautulli metadata cache file Contains media/playback-derived data
JELLYSTAT_CACHE_PATH filesystem path app default Docker, EXE, local Path for Jellystat cache file Contains media/playback-derived data
PLEX_CACHE_PATH filesystem path app default Docker, EXE, local Path for Plex cache file Contains media/playback-derived data
SONARR_CACHE_PATH filesystem path app default Docker, EXE, local Path for Sonarr cache file Contains media-derived data
RADARR_CACHE_PATH filesystem path app default Docker, EXE, local Path for Radarr cache file Contains media-derived data
Performance, paging, retries, and refresh behavior
Variable/arg name Allowed values Default Scope (Docker/EXE/local) Effect Security notes
CACHE_SECONDS integer >=0 300 Docker, EXE, local Main ARR cache TTL none
TAUTULLI_TIMEOUT_SECONDS integer >=0 60 Docker, EXE, local Tautulli request timeout none
TAUTULLI_FETCH_SECONDS integer >=0 0 Docker, EXE, local Tautulli fetch idle delay behavior none
TAUTULLI_METADATA_WORKERS integer >=1 4 Docker, EXE, local Tautulli metadata worker threads none
TAUTULLI_METADATA_SAVE_EVERY integer >=1 250 Docker, EXE, local Save cadence for Tautulli metadata cache updates none
TAUTULLI_REFRESH_STALE_SECONDS integer >=0 3600 Docker, EXE, local Stale lock window for Tautulli refresh marker none
JELLYSTAT_TIMEOUT_SECONDS integer >=0 60 Docker, EXE, local Jellystat request timeout none
JELLYSTAT_STATS_PAGE_SIZE integer >=1 500 Docker, EXE, local Jellystat stats page size none
JELLYSTAT_HISTORY_PAGE_SIZE integer >=1 250 Docker, EXE, local Jellystat history page size none
JELLYSTAT_REFRESH_STALE_SECONDS integer >=0 3600 Docker, EXE, local Stale lock window for Jellystat refresh marker none
PLEX_HISTORY_PAGE_SIZE integer >=1 200 Docker, EXE, local Plex history page size none
PLEX_HISTORY_LAST_VIEWED_AT integer epoch seconds >=0 0 Docker, EXE, local Plex history lower-bound cursor none
PLEX_REFRESH_STALE_SECONDS integer >=0 3600 Docker, EXE, local Stale lock window for Plex refresh marker none
SONARR_TIMEOUT_SECONDS integer >=0 90 Docker, EXE, local Sonarr API timeout none
SONARR_EPISODEFILE_WORKERS integer >=1 8 Docker, EXE, local Sonarr episode-file worker threads none
SONARR_EPISODEFILE_DELAY_SECONDS float >=0 0.0 Docker, EXE, local Inter-request delay for Sonarr episode-file fetches none
SONARR_EPISODEFILE_CACHE_SECONDS integer >=0 600 Docker, EXE, local TTL for Sonarr episode-file side cache none
SONARR_EPISODEFILE_MODE series, bulk/all, fast/stats/skip/off/none series Docker, EXE, local Controls episode-file enrichment strategy fast may reduce detail fidelity
RADARR_TIMEOUT_SECONDS integer >=0 90 Docker, EXE, local Radarr API timeout none
RADARR_WANTED_WORKERS integer 1..4 2 Docker, EXE, local Workers for Radarr wanted/missing fetch path none
RADARR_INSTANCE_WORKERS integer >=1 1 Docker, EXE, local Parallel Radarr instance workers none
ARR_BACKOFF_BASE_SECONDS float >=0 0.5 Docker, EXE, local Initial request retry backoff none
ARR_BACKOFF_MAX_RETRIES integer >=0 2 Docker, EXE, local Max retries for Arr requests none
ARR_CIRCUIT_FAIL_THRESHOLD integer >=1 3 Docker, EXE, local Failures before opening Arr circuit breaker none
ARR_CIRCUIT_OPEN_SECONDS integer >=0 30 Docker, EXE, local Circuit-open hold time before retry attempts none
ARR_HISTORY_CACHE_TTL_SECONDS integer >=30 180 Docker, EXE, local In-memory history cache TTL none
ARR_FILENAME_CACHE_TTL_SECONDS integer >=0 1800 Docker, EXE, local Filename cache TTL none
ARR_FILENAME_CACHE_MAX_ENTRIES integer >=1 5000 Docker, EXE, local Filename cache max entries none
Secret variable companion patterns (applies to startup)

For each sensitive value variable below, Sortarr supports companion source selectors:

  • {VAR}_FILE: file-based secret source
  • {VAR}_CRED_TARGET: Windows Credential Manager target
  • VAR=wincred:<target> inline credential reference (Windows, compatibility mode; deprecated in docs/UI)

Sensitive variable families:

  • SONARR_API_KEY, SONARR_API_KEY_2, SONARR_API_KEY_3
  • RADARR_API_KEY, RADARR_API_KEY_2, RADARR_API_KEY_3
  • TAUTULLI_API_KEY
  • JELLYSTAT_API_KEY
  • PLEX_TOKEN
  • BASIC_AUTH_PASS
  • SORTARR_SECRET_KEY (plus legacy aliases SECRET_KEY, SECRET_KEY_FILE)

Rule of thumb: configure exactly one secret source per variable (VAR, VAR_FILE, or VAR_CRED_TARGET).

NOTE

Reverse proxy / HTTPS (Traefik, Nginx, Cloudflare Tunnel, etc.)

Sortarr can run behind a reverse proxy. In that case it may need to trust X-Forwarded-* headers so Flask correctly detects the external scheme/host (for example https://sortarr.mydomain.com). If this is not set correctly, you may see errors like "CSRF origin mismatch" during setup when accessing Sortarr via HTTPS through a proxy.

  • SORTARR_PROXY_MODE (recommended)
  • Setup path: Settings > Advanced settings > Network & CSRF > Proxy mode

Typical proxy modes:

  • direct No trusted proxy headers
  • single One reverse proxy hop
  • double Two reverse proxy hops (for example Cloudflare Tunnel -> Traefik -> Sortarr)
  • custom Use explicit per-header hop counts

When proxy trust is enabled, Sortarr applies trust differently per header:

  • X-Forwarded-For uses the configured hop count for client IP trust
  • X-Forwarded-Host, X-Forwarded-Proto, and X-Forwarded-Port default to one trusted forwarded value unless overridden
  • X-Forwarded-Prefix defaults to 0 and should only be enabled explicitly for path-prefix proxy setups

This matches common proxy chains where every hop appends to X-Forwarded-For, but only the outermost proxy sets Host / Proto and the inner proxy passes them through unchanged.

If your proxies emit a different number of values per header, use SORTARR_PROXY_MODE=custom and override them individually:

  • SORTARR_PROXY_HOPS
  • SORTARR_PROXY_HOPS_FOR
  • SORTARR_PROXY_HOPS_HOST
  • SORTARR_PROXY_HOPS_PROTO
  • SORTARR_PROXY_HOPS_PORT
  • SORTARR_PROXY_HOPS_PREFIX

CSRF origin policy:

  • Same-host origin matching is the default and preferred mode.
  • SORTARR_CSRF_TRUSTED_ORIGINS is an exact-origin fallback (scheme://host[:port]) for token-gated cross-origin submissions.
  • Cross-host trusted origins require explicit opt-in with SORTARR_ALLOW_CROSS_HOST_TRUSTED_ORIGINS=1.
  • Trusted-origin fallback is still CSRF-token gated; it is not a token bypass.

Diagnostics:

  • GET /api/diagnostics/csrf reports how the current request origin/cookie policy is being evaluated.
  • GET /api/diagnostics/security-self-check summarizes the current security posture, including proxy and CSRF settings.

Example for Cloudflare Tunnel -> Caddy -> Sortarr:

SORTARR_PROXY_MODE=double
SORTARR_PROXY_HOPS_FOR=2
SORTARR_PROXY_HOPS_HOST=1
SORTARR_PROXY_HOPS_PROTO=1

Security note: If proxy trust is enabled, make sure Sortarr is only reachable through your reverse proxy. Do not publish the Sortarr container port directly to the internet.

Requirements and notes:

  • Choose one auth boundary, not two:
    • Basic means Sortarr handles login itself.
    • External means your reverse proxy handles login and Sortarr trusts a verified upstream identity header from that proxy.
  • Direct installs and ordinary reverse proxies should normally stay on Basic.
  • If your reverse proxy already does its own login, switch Sortarr to External instead of stacking two separate Basic Auth layers.
  • Docker image runs a single Waitress process with multiple threads (default --threads=4) to keep cache/refresh state consistent
  • Designed for on-demand queries with persistent caching; the UI only polls while playback matching finishes
  • No database or media filesystem access required

Buttons and their actions:

  • Refresh Sonarr/Radarr data reloads only the active tab's data and updates the persistent cache.
  • Refresh playback data reloads both Sonarr and Radarr (and playback if configured) and updates the persistent cache, then rebuilds playback matching using cached provider data or starts a playback refresh when needed.
  • Clear caches & rebuild clears all cache files and starts a full rebuild, similar to a cold start. Recommended when inaccurate data is spotted.
  • Reset UI clears local UI settings and reloads using the cached data.
  • Fullscreen Data Table (⛶) hides panels and expands the table to fill the screen (press Escape or ✕ to exit).

Notes continued:

  • Sonarr/Radarr API keys are required for setup. Read-only keys are recommended.

  • Playback stats are optional; you can configure multiple history sources (Tautulli/Jellystat/Plex) and choose a preferred history source in Setup

  • Sonarr/Radarr are optional when Plex is configured as the preferred media source

  • Plex can be configured alongside the history source for provider insights

  • Provider option set model:

    • Media sources can be arr, plex, or both.
    • History/playback sources can be any combination of tautulli, jellystat, and plex.
    • Effective media source follows MEDIA_SOURCE_PREFERENCE (arr, plex, auto).
    • Effective history source follows HISTORY_SOURCE_PREFERENCE (tautulli, jellystat, plex, auto).
    • auto selects the first available configured source using Sortarr's deterministic order.
  • Tautulli metadata lookups are cached to disk for faster matching; adjust workers/save cadence as needed.

  • TAUTULLI_METADATA_LOOKUP_LIMIT and TAUTULLI_METADATA_LOOKUP_SECONDS are internal/enforced startup values (not user tuning knobs).

  • Stale Tautulli refresh locks clear after TAUTULLI_REFRESH_STALE_SECONDS to avoid stuck matching

  • On first run after an upgrade, Sortarr clears caches and drops legacy Tautulli default env values so new defaults apply

  • When Tautulli matching runs in the background, the UI auto-refreshes to apply playback stats

  • When PUID/PGID are set, the container runs as that user and will chown the config/cache paths on startup. Useful for Unraid users.

  • Treat Sortarr.env as a secret; it stores API keys and optional basic auth credentials

  • State-changing API requests require X-CSRF-Token matching the sortarr_csrf cookie (handled automatically by the UI).

  • If you see CSRF origin mismatch, set SORTARR_PROXY_MODE first, then use SORTARR_PROXY_HOPS* only for custom proxy chains.

  • Proxy edge-case fallbacks (origin-scheme-fallback-accepted, referer-scheme-fallback-accepted, and trusted-origins via SORTARR_CSRF_TRUSTED_ORIGINS) still require a valid CSRF token and are logged as warnings.

Troubleshooting

CSRF and Reverse Proxy Issues

If you access Sortarr through a reverse proxy (e.g., Traefik, Nginx, Cloudflare Tunnel) and encounter CSRF origin mismatch, Sortarr is usually seeing a host/scheme mismatch between browser Origin and effective request URL.

If you are still on 0.8.5.1 or earlier and see:

  • CSRF origin mismatch
  • token-missing-cookie
  • CSRF origin mismatch on every Save

upgrade to 0.8.6 first. That release fixes the Waitress forwarded-header regression, request-aware setup/session cookie security, and failed-save setup persistence.

Preferred fix path in UI:

  • Open Settings > Advanced settings > Network & CSRF.
  • Pick the correct Proxy mode (Direct, Single proxy, Two proxies, Custom).
  • Save, then retest Test connection / Save and continue.

If Setup diagnostics show blank forwarded headers (X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-For):

  • Confirm requests are reaching Sortarr through the expected proxy chain.
  • Verify your proxy middleware/forward-auth stack is preserving X-Forwarded-* headers.
  • Fix header forwarding first, set SORTARR_PROXY_MODE (direct|single|double), then tune SORTARR_PROXY_HOPS* only if you need custom behavior.

If you access Sortarr directly over plain HTTP and see token-missing-cookie:

  • remove any stale https://... value from SORTARR_PUBLIC_HOST, SORTARR_PUBLIC_URL, or SORTARR_PUBLIC_ORIGIN
  • or set SORTARR_SESSION_COOKIE_SECURE=0
  • then reload Setup and retry

If your reverse proxy already does its own login and Sortarr still prompts for Basic Auth or rejects credentials:

  • leave proxy mode as normal (single, double, or custom)
  • switch Authentication Method to External
  • set SORTARR_WAITRESS_TRUSTED_PROXY to the immediate upstream proxy
  • set SORTARR_UPSTREAM_AUTH_HEADER to the verified identity header your proxy injects

The Fix: SORTARR_PROXY_MODE

Set proxy trust so Sortarr can reconstruct the external URL from X-Forwarded-* headers.

  • Set SORTARR_PROXY_MODE=single if you have a single proxy (most common).
  • Set SORTARR_PROXY_MODE=double if you have a double proxy setup (e.g., Cloudflare Tunnel → Traefik → Sortarr).

Important: Header counts can differ by header type

Not every X-Forwarded-* header necessarily grows at every hop.

Common pattern:

  • X-Forwarded-For gets one new entry per proxy
  • X-Forwarded-Host and X-Forwarded-Proto are often set once by the outermost proxy and then forwarded unchanged by inner proxies

Sortarr default behavior:

  • SORTARR_PROXY_MODE selects baseline trust behavior (direct, single, double)
  • SORTARR_PROXY_HOPS controls X-Forwarded-For when you need custom trust counts
  • X-Forwarded-Host, Proto, Port, and Prefix default to trusting only one forwarded value when proxy mode is enabled

If your setup needs something else, override the trust count per header:

  • SORTARR_PROXY_HOPS_FOR
  • SORTARR_PROXY_HOPS_HOST
  • SORTARR_PROXY_HOPS_PROTO
  • SORTARR_PROXY_HOPS_PORT
  • SORTARR_PROXY_HOPS_PREFIX

Example:

If Caddy forwards:

  • X-Forwarded-For: CLIENT_IP, CLOUDFLARE_IP
  • X-Forwarded-Host: sortarr.example.com
  • X-Forwarded-Proto: https

Use:

SORTARR_PROXY_MODE=double
SORTARR_PROXY_HOPS=2

Effective trust:

  • x_for=2
  • x_host=1
  • x_proto=1

If needed, make it explicit:

SORTARR_PROXY_MODE=custom
SORTARR_PROXY_HOPS=2
SORTARR_PROXY_HOPS_FOR=2
SORTARR_PROXY_HOPS_HOST=1
SORTARR_PROXY_HOPS_PROTO=1

Proxy Cookbook

Use this cookbook when you want a known-good starting point instead of guessing at proxy hops.

Rules that stay the same across all supported layouts:

  • Set SORTARR_WAITRESS_TRUSTED_PROXY to the immediate upstream proxy that talks directly to Sortarr.
  • In Docker, that is usually the proxy container or service name on the shared network (traefik, caddy, nginx).
  • Save proxy settings in Setup, then restart Sortarr so Waitress picks up the new trust settings.
  • Let X-Forwarded-For grow with hop depth, but keep X-Forwarded-Host, X-Forwarded-Proto, and X-Forwarded-Port to a single value.
  • Traefik forwardAuth middleware does not count as an extra Sortarr proxy hop by itself.
Recommended settings by chain
Chain Recommended Sortarr settings SORTARR_WAITRESS_TRUSTED_PROXY Expected forwarded header shape
Direct / no proxy SORTARR_PROXY_MODE=direct blank X-Forwarded-* absent
Traefik -> Sortarr SORTARR_PROXY_MODE=single traefik or the Traefik IP X-Forwarded-For = 1 value; Host = 1; Proto = 1; Port = 0 or 1
Caddy -> Sortarr SORTARR_PROXY_MODE=single caddy or the Caddy IP X-Forwarded-For = 1 value; Host = 1; Proto = 1; Port = 0 or 1
Nginx -> Sortarr SORTARR_PROXY_MODE=single nginx or the Nginx IP X-Forwarded-For = 1 value; Host = 1; Proto = 1; Port = 0 or 1
Cloudflare Tunnel -> Traefik -> Sortarr SORTARR_PROXY_MODE=double traefik or the Traefik IP X-Forwarded-For = 2 values; Host = 1; Proto = 1; Port = 0 or 1
Cloudflare Tunnel -> Caddy -> Sortarr SORTARR_PROXY_MODE=double caddy or the Caddy IP X-Forwarded-For = 2 values; Host = 1; Proto = 1; Port = 0 or 1
Cloudflare Tunnel -> Nginx -> Sortarr SORTARR_PROXY_MODE=double nginx or the Nginx IP X-Forwarded-For = 2 values; Host = 1; Proto = 1; Port = 0 or 1
Traefik forwardAuth -> Sortarr SORTARR_PROXY_MODE=single traefik or the Traefik IP Same as single-proxy; auth middleware does not add a hop
Cloudflare Tunnel -> Traefik forwardAuth -> Sortarr SORTARR_PROXY_MODE=double traefik or the Traefik IP Same as double-proxy; auth middleware does not add a hop
Three proxies (for example Cloudflare Tunnel -> Traefik -> Caddy -> Sortarr) SORTARR_PROXY_MODE=custom
SORTARR_PROXY_HOPS=3
SORTARR_PROXY_HOPS_FOR=3
SORTARR_PROXY_HOPS_HOST=1
SORTARR_PROXY_HOPS_PROTO=1
SORTARR_PROXY_HOPS_PORT=1
The last proxy before Sortarr (caddy in this example) X-Forwarded-For = 3 values; Host = 1; Proto = 1; Port = 0 or 1
Path-prefix publish (for example /sortarr) Start with the normal chain above, then add SORTARR_PROXY_MODE=custom and SORTARR_PROXY_HOPS_PREFIX=1. Leave prefix trust off unless you actually publish Sortarr under a path prefix. The last proxy before Sortarr X-Forwarded-Prefix = 1 value; keep Host / Proto / Port single-valued
Expected forwarded-header shapes

Healthy requests usually look like this:

  • Single proxy:
    • X-Forwarded-For: 203.0.113.10
    • X-Forwarded-Host: sortarr.example.com
    • X-Forwarded-Proto: https
    • X-Forwarded-Port: 443 or blank
  • Double proxy:
    • X-Forwarded-For: 203.0.113.10, 172.21.0.5
    • X-Forwarded-Host: sortarr.example.com
    • X-Forwarded-Proto: https
    • X-Forwarded-Port: 443 or blank
  • Triple / custom:
    • X-Forwarded-For has one entry per proxy hop
    • X-Forwarded-Host stays single-valued
    • X-Forwarded-Proto stays single-valued
    • X-Forwarded-Port stays single-valued or blank
Known-good Traefik snippet

Traefik's normal forwarding behavior is already compatible with Sortarr. The critical part is trusting only the previous proxy or network on the entrypoint. If you use forwardAuth, keep trustForwardHeader: true; it does not change Sortarr hop count.

traefik.yml

entryPoints:
  websecure:
    address: ":443"
    forwardedHeaders:
      trustedIPs:
        - "172.18.0.0/16"
        - "127.0.0.1/32"

providers:
  file:
    filename: "/etc/traefik/dynamic.yml"

dynamic.yml

http:
  routers:
    sortarr:
      rule: "Host(`sortarr.example.com`)"
      entryPoints:
        - websecure
      tls: {}
      service: sortarr
      # Uncomment if you use Traefik forward-auth. This does not add a Sortarr proxy hop.
      # middlewares:
      #   - auth

  # middlewares:
  #   auth:
  #     forwardAuth:
  #       address: "http://auth:4181/auth"
  #       trustForwardHeader: true

  services:
    sortarr:
      loadBalancer:
        servers:
          - url: "http://sortarr:8787"
Known-good Caddy snippet

This pattern is safe both when Caddy is the public TLS edge and when Caddy sits behind another trusted proxy such as Cloudflare Tunnel. It preserves incoming single-value Host / Proto / Port when they already exist, otherwise it derives them from the current request.

{
    admin off
    servers {
        trusted_proxies static private_ranges 172.18.0.0/16 127.0.0.1/32
    }
}

https://sortarr.example.com {
    map {http.request.header.X-Forwarded-Host} {sortarr_forwarded_host} {
        "" {http.request.host}
        default {http.request.header.X-Forwarded-Host}
    }
    map {http.request.header.X-Forwarded-Proto} {sortarr_forwarded_proto} {
        "" {scheme}
        default {http.request.header.X-Forwarded-Proto}
    }
    map {http.request.header.X-Forwarded-Port} {sortarr_forwarded_port} {
        "" {http.request.port}
        default {http.request.header.X-Forwarded-Port}
    }
    reverse_proxy sortarr:8787 {
        header_up Host {sortarr_forwarded_host}
        header_up X-Forwarded-Host {sortarr_forwarded_host}
        header_up X-Forwarded-Proto {sortarr_forwarded_proto}
        header_up X-Forwarded-Port {sortarr_forwarded_port}
    }
}
Known-good Nginx snippet

This Nginx pattern does the same normalization as the Caddy example: preserve existing single-value forwarded headers if a trusted upstream already set them, otherwise derive them locally and append only X-Forwarded-For.

http {
    map $http_x_forwarded_host $sortarr_forwarded_host {
        default $http_x_forwarded_host;
        "" $http_host;
    }
    map $http_x_forwarded_proto $sortarr_forwarded_proto {
        default $http_x_forwarded_proto;
        "" $scheme;
    }
    map $http_x_forwarded_port $sortarr_forwarded_port {
        default $http_x_forwarded_port;
        "" $server_port;
    }

    server {
        listen 443 ssl;
        server_name sortarr.example.com;
        ssl_certificate /etc/nginx/certs/fullchain.pem;
        ssl_certificate_key /etc/nginx/certs/privkey.pem;

        location / {
            proxy_set_header Host $sortarr_forwarded_host;
            proxy_set_header X-Forwarded-Host $sortarr_forwarded_host;
            proxy_set_header X-Forwarded-Proto $sortarr_forwarded_proto;
            proxy_set_header X-Forwarded-Port $sortarr_forwarded_port;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://sortarr:8787;
        }
    }
}
Bad header shapes

These shapes cause problems and should be normalized at the immediate proxy:

  • Bad:
    • X-Forwarded-Proto: https, https
    • X-Forwarded-Port: 443, 443
  • Good:
    • X-Forwarded-Proto: https
    • X-Forwarded-Port: 443
    • X-Forwarded-For: 203.0.113.10, 172.21.0.5

Why this matters:

  • Waitress 3.x can reject comma-separated X-Forwarded-Proto or X-Forwarded-Port when those headers are trusted.
  • Sortarr diagnostics now warn about this explicitly so it does not look like a random CSRF failure.
  • Only X-Forwarded-For should reflect the full proxy chain. Host / Proto / Port should stay single-valued.

Security Note

Only enable proxy trust if Sortarr is behind a trusted reverse proxy. Do not expose the Sortarr container port directly to the internet when proxy trust is enabled, as it allows clients to spoof their origin via headers.

Session Secret Key and Ephemeral Warning

Sortarr uses a secret key to sign sessions and bind CSRF tokens to session context.

  • Persistent session-secret references are required for normal configured operation.
  • First bootstrap may use a temporary ephemeral session secret before the first successful Setup save.
  • Configured startup aborts if the persistent session secret cannot be resolved and unsafe recovery is not enabled.
  • SORTARR_ALLOW_UNSAFE_EPHEMERAL_RECOVERY=1 is a temporary recovery override for lockout scenarios only.
  • Plaintext session-secret values are migration inputs only. Use SORTARR_SECRET_KEY_FILE, SORTARR_SECRET_KEY_CRED_TARGET, or Windows wincred: references for steady-state runtime configuration.
  • Saving setup applies the configured secret key immediately at runtime, so the bootstrap warning clears without requiring restart.

Mismatch Center

The Mismatch Center is a dedicated tool for identifying and resolving discrepancies between your media libraries (Sonarr/Radarr) and your playback providers (Plex, Tautulli, or Jellystat). It is accessible via the Mismatch Center button in the main toolbar (visible when at least two providers are configured).

Mismatch Center

Purpose and Functionality

  • Cross-Provider Validation: Compares metadata IDs (TVDB, TMDB, IMDB) and titles across all active providers to surface mismatches.
  • Root Cause Analysis: Categorizes issues into "Provider Conflicts" (where different providers report different IDs) or "Unmatched/Skipped" states.
  • Diagnostic Reasons: Displays specific reasons for a failure, such as "Missing ID in Plex" or "Title mismatch in Tautulli".

Filtering and Organization

  • Provider Filter: Isolate issues to a specific provider (e.g., show only items that failed to match in Tautulli).
  • Status Filter: View items based on their match state:
    • Unmatched: No match was found using the available IDs.
    • Skipped: The item was intentionally skipped due to configuration limits (e.g., Tautulli lookup limit).
    • Unavailable: The provider returned no data for the item.
  • Grouping: Group the results by Provider, Status, Reason, or Category to identify patterns (e.g., grouping by Reason might show a large number of items failing due to a specific library configuration issue).
  • Search: Quickly find specific titles or paths within the mismatch list.

Exporting for Resolution

Use the Export CSV button to download the current filtered view. This is useful for:

  • Creating a "to-do" list for manual metadata fixes in Plex or Tautulli.
  • Sharing mismatch patterns with the development team for bug reporting.

Tautulli Matches

If a title shows missing or incorrect Tautulli stats, the fastest fix is to refresh the item inside Tautulli after you confirm it is matched correctly in Arr and Plex.

If you're using Plex as the playback provider (no Tautulli/Jellystat), confirm the Plex metadata IDs are correct and that Plex has recent playback history for the item.

Steps:

  1. In Sonarr/Radarr, confirm the title/year and IDs are correct for the item.
  2. In Plex, confirm the item metadata (title/year/IDs) matches Sonarr/Radarr.
  3. In Plex, play the item for 30-60 seconds (this creates fresh Tautulli history).
  4. In Tautulli, open the Now Playing entry and confirm it is the correct title.
  5. In Tautulli, go to the matching library and search for the item by name.
  6. If it is missing or stale, open the item and choose Media Info -> Refresh Media Info.
  7. After the refresh completes, search again to confirm the item appears.
  8. Back in Sortarr, click Refresh all data and wait for the load to complete.

If the title still does not match, the issue is likely missing IDs in Plex/Tautulli or a mismatch between libraries. Share the title and the Tautulli history count for that item so we can investigate further.

Tautulli UI totals vs Sortarr totals

Sortarr reads totals from the Tautulli History API (full play history). The Tautulli UI can show different totals because:

  • The show page "Global Stats" totals are built from the Tautulli metadata cache, which can be incomplete for large/old libraries or after lookup limits.
  • The History page footer shows the total for the current page of results, not the full filtered set.

If the show page totals look low, rebuild Tautulli's metadata cache:

  1. Stop Tautulli.
  2. In Tautulli, open Settings and note the "Cache Directory: X" path, then back up that directory before clearing it.
  3. Start Tautulli.
  4. Refresh the library in Tautulli and wait for metadata to rebuild.

After the rebuild, the show page totals should align with History totals (and with Sortarr).

User Interface and Workflows

Sortarr provides a flexible interface for analyzing large media libraries through advanced filtering, virtualization, and focused views.

Main Shows View

Filtering

Sortarr supports multiple filtering modes to help you isolate specific subsets of your library.

Filtering and Quick Chips

Simple and Advanced Input

  • Title/Path Filters: Basic wildcard-supported inputs (* and ?) for quick searching.
  • Advanced Filter Input: Supports a field:value syntax for complex queries.
    • Syntax: Use field:value for standard matches (is/contains) or field>=value / field<=value for numeric comparisons.
    • Numeric Buckets: For fields like gbperhour or totalsize, integer values match whole-number buckets (e.g., gbperhour:1 matches 1.0 to 1.99).
    • Wildcards: Supports * and ? for string fields.

Filter Builder

The Filter Builder provides a structured way to create complex queries without remembering field names. It is toggled via the Chips button in the filter panel footer.

  1. Category: Select the metadata field (e.g., Audio Language, Resolution, Size). The list is searchable and alphabetized.
  2. Condition: Select a comparison operator (e.g., "is", "contains", "at least").
  3. Value: Enter a custom value or select from a dropdown of suggested values.
  4. Active Filters: Each added filter appears as a "bubble" (chip) below the builder. Clicking a bubble removes it.

Quick Chips

Quick chips provide one-click access to common filters like "Missing", "Cutoff Unmet", "Atmos", and "4K". They are grouped by category (Audio, Resolution, Source, etc.) and react to the current library tab.

Table Workflows

Fullscreen Mode (⛶)

The Fullscreen Data Table button expands the table to fill the entire browser viewport, hiding the toolbar and filter panels to maximize data visibility.

  • Persistence: Fullscreen state is saved to local storage and persists across reloads.
  • Exit: Click the button in the top-right or press Escape.

Column Visibility

Use the Columns menu to toggle visibility for dozens of metadata fields.

  • Search: Quickly find columns by name.
  • Group Toggles: Toggle entire groups like "Sonarr", "Video", or "Playback".
  • CSV Columns: Enable advanced columns (e.g., Title Slug, TMDB ID) specifically for CSV export.

CSV Export

Exports the current filtered view of the active tab (Shows or Movies) to a compressed CSV file.

Main Movies View

  • Scope: Includes all currently visible columns.
  • Playback Data: Playback-specific columns are included only if a playback provider (Tautulli, Jellystat, or Plex) is configured.

Media Metadata: Audio and Subtitles

Sortarr extracts and normalizes advanced media metadata from your media files via the Sonarr and Radarr APIs.

Language Detection and Normalization

  • Automatic Mapping: 3-letter ISO codes (e.g., eng, deu) and 2-letter codes (e.g., en, de) are automatically mapped to full language names (e.g., "English", "German") for display and filtering.
  • Mixed Languages: If a file contains multiple audio or subtitle tracks, Sortarr identifies them as "Mixed" and provides columns (Audio Languages All, Subtitle Languages All) to see the full list.
  • Normalization: Language labels are normalized (e.g., English+Spanish) to ensure consistent filtering regardless of how the source Arr instance reports them.

Columns and Filters

  • Audio Languages: Displays the primary audio language. Filtering supports audiolang:eng or structured selection in the Filter Builder.
  • Subtitle Languages: Displays the primary subtitle language. Use sublang:eng or the "Subtitles" quick chips.
  • Audio Codec & Channels: Specific columns for codecs (e.g., Atmos, EAC3, TrueHD) and channel counts (e.g., 5.1, 7.1, Stereo).
  • No Subtitles: A dedicated filter (nosubs:true) and quick chip to find media missing subtitle tracks.

Quick Chips

One-click filters for common languages and codecs are available in the Quick Chips panel:

  • Audio Language: English, Spanish, French, German, Japanese.
  • Subtitles: English, Spanish, French, German, Japanese, No Subtitles.
  • Audio Codec: Atmos, DD+, TrueHD, 5.1+, 7.1+, Stereo.

Plex Insights Drawer

The Plex Insights Drawer provides a high-level overview of your Plex server's status and library health. It is opened via the Plex Insights button in the main toolbar (visible only when Plex is configured).

Plex Insights

Hubs and Navigation

  • Library Selection: Use the chips at the top to filter insights by a specific Plex library (e.g., "Movies", "TV Shows") or view "All Libraries".
  • Hub Chips: Switch between different metadata hubs like "Recently Added", "Recently Viewed", or "On Deck".
  • Hub Items: Displays a list of items from the selected hub, including title, year, view count, and last viewed date.

Match Health Summary

Provides a breakdown of how well your Sonarr and Radarr libraries match your Plex library.

  • Statuses: Counts for Matched, Unmatched, Skipped, Unavailable, and Pending items.
  • Matched by: Lists the metadata fields used for successful matches (e.g., "Matched by TVDB ID").
  • Top reasons: Surfaces common reasons for mismatches or skips.

Server Activities and Maintenance

  • Activities: Real-time display of active Plex server tasks, such as library scans, metadata refreshes, or media analysis, including progress percentages.
  • Butler: Monitoring of Plex Butler tasks (maintenance tasks like "Clean Old Cache Files" or "Refresh Local Metadata") with their current status and schedule.

Live Updates

The Live button in the drawer header toggles real-time updates. When enabled, Sortarr uses Server-Sent Events (SSE) to listen for Plex activity and automatically refreshes the insights when changes occur.

Grab and Import History

Sortarr provides a detailed view of the lifecycle of your media files via the History Drawer. This is useful for tracking when an item was grabbed, imported, or if a download failed.

History Drawer

Accessing History

  • Trigger: Click the H icon (or clock icon) next to an episode title in the expanded Sonarr series view, or next to a movie title in the Radarr view.
  • Scope: The history is specific to the selected episode or movie across all configured Arr instances.

Summary View (Latest vs. Previous)

When first opened, the drawer shows a summary comparing the current (Latest) file event with the one immediately preceding it (Previous).

  • Event Type: Displays if the item was "Grabbed", "Imported", "Renamed", "Deleted", or "Failed".
  • Deltas: Highlights changes in File Size and Custom Format Score between the two events. For example, CF +20 or +1.2 GB.
  • Metadata: Shows the quality (resolution/source), date of the event, and the original source title.

Event Timeline

Clicking Show all events expands the drawer to show the full history retrieved from the Arr API.

  • Event List: A chronological list of all relevant events for that item.
  • Detailed Metadata: Includes:
    • Quality: The specific quality profile used (e.g., Web-DL 1080p).
    • Release Group (RG): The group responsible for the release.
    • Download ID (DL): The internal identifier for the download client task.
    • Languages: Lists the audio/subtitle languages identified during import.

Exporting History

Use the Export CSV button in the drawer header to download the full history for the item as a compressed CSV file, including raw data fields for advanced diagnostics.

UI Status Indicators

Sortarr uses visual indicators to quickly surface the health of your media instances and the status of playback data matching.

Instance Health Badges

Health badges appear in the filter panel footer when a configured Sonarr or Radarr instance reports an issue.

  • Error (Red): Critical issues, such as an unreachable instance or authentication failure.
  • Warning (Orange): Non-critical issues, such as indexers being down or missing root folders.
  • Notice (Blue): Informational alerts or minor configuration recommendations.
  • Dismissal: Clicking the × on a badge hides it for the current session. Sortarr remembers dismissed alerts to prevent repetitive clutter.
  • Details: For long messages, an i button provides the full text of the alert.

Playback Match Orbs

Small colored "orbs" appear next to titles in the main table when a playback provider (Tautulli, Jellystat, or Plex) is enabled. Hovering over an orb displays the specific match reason.

  • Matched (Green): The item was successfully identified in the playback history via metadata IDs or title matching.
  • Potential Mismatch (Orange): An item was found but metadata IDs do not match, or multiple potential matches were found.
  • Future Release (Purple): Matching was skipped because the item has not yet aired or been released.
  • Not on Disk (Red): Matching was skipped because the item is missing from your local storage.
  • Not Checked / Unavailable (Gray): Matching has not been performed for this item, or the provider has no data.

Developer and Integration Notes

Sortarr provides a read-only HTTP API that powers the frontend and can be used for external integrations or custom dashboards.

Authentication

Sortarr has two auth modes:

  • basic: Sortarr handles login itself. /api/* endpoints require Basic Authentication when BASIC_AUTH_USER and the matching password are configured.
  • external: your trusted reverse proxy handles login and Sortarr trusts the configured upstream identity header (default X-Forwarded-User) from that proxy. Direct spoofed headers are rejected.

If you use basic, include the Authorization: Basic <credentials> header in your requests. If you use external, send requests through the trusted proxy boundary rather than directly to the Sortarr port.

CSRF Protection

For state-changing requests (POST, PUT, PATCH, DELETE), Sortarr enforces CSRF protection.

  • Required Header: X-CSRF-Token
  • Matching Cookie: The header value must match the value of the sortarr_csrf cookie.
  • Origin Policy: Origin or Referer must match the current request origin unless an exact trusted origin is explicitly configured.
  • Trusted Origins: Trusted-origin fallback remains token-gated and same-host by default; cross-host trust requires explicit opt-in.
  • Validation: Requests without a matching token or allowed origin will return 403 Forbidden ("CSRF validation failed.").

Key API Endpoints

Endpoint Method Description
/api/shows GET Returns a JSON array of all series from Sonarr, enriched with playback stats if configured.
/api/movies GET Returns a JSON array of all movies from Radarr, enriched with playback stats if configured.
/api/shows.csv GET Returns series data in CSV format.
/api/movies.csv GET Returns movie data in CSV format.
/api/status GET Returns the status of the data load and playback matching. Supports lite=1 for a faster response with fewer details.
/api/config GET Returns the current sanitized application configuration (e.g., instance URLs, active providers).
/api/sonarr/refresh POST Triggers a metadata and library refresh for Sonarr. Can target a specific seriesId.
/api/radarr/refresh POST Triggers a metadata and library refresh for Radarr. Can target a specific movieId.
/api/tautulli/refresh POST Triggers a playback provider (e.g., Tautulli) library refresh.
/api/sonarr/health GET Returns health check alerts from all configured Sonarr instances.
/api/radarr/health GET Returns health check alerts from all configured Radarr instances.
/api/history GET Returns grab/import history for a specific item. Requires app (sonarr/radarr) and seriesId/episodeId or movieId.
/api/version GET Returns the current Sortarr version.

Error Handling

If an API request to a backend provider (Sonarr/Radarr) fails, Sortarr returns a 502 Bad Gateway error. A sanitized hint about the failure is provided in the X-Sortarr-Error response header.

Planned features

  • Expanded efficiency views and comparisons
  • Performance improvements for very large libraries
  • More grouping and filtering options

Feedback and ideas are welcome.

Performance tools

Perf overlay

Press Ctrl+Shift+P to toggle a lightweight perf overlay (FPS estimate, long task count when supported, render time, visible rows, and DOM counts).

Benchmark mode (synthetic data)

You can render synthetic datasets without connecting Sonarr/Radarr:

  • ?bench=1&app=radarr&rows=1000
  • ?bench=1&app=radarr&rows=5000
  • ?bench=1&app=radarr&rows=20000
  • Add &wide=1 to show more columns.

Image previews (optional)

Sortarr can display small poster previews streamed directly from Sonarr and Radarr:

  • Sonarr: series poster appears only inside the expanded series panel header.
  • Radarr: movie poster appears as a hover tooltip (table row heights do not change).

Disable all image loading with ?images=0.

Clone this wiki locally