Collapsarr
Never get stuck without a downmix again.
Collapsarr is a companion application for Sonarr and Radarr. It watches your library for media missing a lower-channel-count audio track — a 7.1 release with no stereo fallback, a 5.1 file your soundbar can't decode — and adds one automatically via FFmpeg, without touching the track that's already there.
Why
- No upmixing, ever. Collapsarr only adds tracks the source can actually support (a stereo/2.1/5.1 downmix from a higher channel count) — it will never fake a 5.1 track out of a stereo source.
- Originals are never at risk. The remux writes to a temp file, validates duration and stream count, then atomically swaps it in. Any failure at any stage leaves the original file completely untouched — no partial writes, no orphaned backups.
- Fits into the *arr stack you already run. Sonarr/Radarr integration (webhooks + periodic scan), a dark UI in the same style as the rest of the family, and a REST API following the same conventions.
Features
- Sonarr and Radarr integration — instance config, connectivity check, remote path mapping, multiple concurrent instances
- Per-target, per-language detection that stacks additional targets without duplicating what's already there
- FFmpeg remux: stream-copies existing tracks, encodes new audio (AAC for Stereo, AC3 @ 448kbps for 2.1/5.1)
- Job queue with configurable concurrency — triggered by webhook, periodic full-library scan, on-demand scan, or manual per-file trigger
- Full job history: status, timestamps, FFmpeg exit code, error text
- Web UI in the same dark theme as Sonarr/Radarr/Bazarr — Wanted view, Activity/History, per-file detail view with a manual trigger
- Webhook + Discord notifications on job failure or app health issues (e.g. FFmpeg missing)
- REST API with *arr-convention auth (API key)
Quick start
Docker (recommended):
services:
collapsarr:
image: odxnsson/collapsarr:latest
container_name: collapsarr
ports:
- "8282:8282"
volumes:
- ./config:/config
- /path/to/media:/media
environment:
- PUID=1000
- PGID=1000
restart: unless-stopped
Then open http://localhost:8282 — you'll land on a one-time credential setup
page (see Authentication for how login is enforced,
including the caveat if you're putting Collapsarr behind a reverse proxy).
restart: unless-stopped above means the container comes back automatically
whenever it stops unexpectedly or the Docker daemon restarts (e.g. after a
host reboot) — see Running on startup if you need it
to survive a reboot on a bare-metal/PyPI install instead.
PyPI (bare-metal):
pipx install collapsarr
collapsarr
No flags, no config file needed — Collapsarr stores its SQLite database
under your platform's standard per-user data directory by default (e.g.
~/.local/share/collapsarr/collapsarr.db on Linux; native per-OS locations
on macOS/Windows), creating it automatically if it doesn't exist. Set
COLLAPSARR_DATA_DIR if you'd rather it live somewhere else — see
Configuration. Requires FFmpeg on PATH — see
Requirements below. Open http://localhost:8282; see
Configuration for the full list of environment variables,
and Running on startup for a systemd unit.
Requirements
-
Python 3.12+
-
FFmpeg — external system dependency, checked at startup and reported on the health page if missing. Bundled in the Docker image; install it yourself for a bare-metal/PyPI setup:
OS Command Debian / Ubuntu sudo apt install ffmpegFedora sudo dnf install ffmpegArch sudo pacman -S ffmpegmacOS (Homebrew) brew install ffmpegWindows (winget) winget install ffmpegVerify with
ffmpeg -version. Official builds/source: ffmpeg.org/download.html.
Authentication
Collapsarr requires a one-time credential setup (/setup, first run) and,
after that, logging in (/login) before the UI/API is usable — except from
a caller Collapsarr considers "local". The Login requirement setting
(Settings → General, auth_required in the API) controls this:
| Mode | Behaviour |
|---|---|
Disabled for local addresses (local_bypass, default) |
A caller connecting from a loopback (127.0.0.1/::1) or private-range (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, etc.) address reaches the UI and API with no setup and no login. Anyone connecting from a routable/public address still has to authenticate normally. |
Always required (enabled) |
Every caller is challenged, regardless of address. |
Reverse-proxy configuration
If Collapsarr sits behind a reverse proxy (nginx, Traefik, Cloudflare Tunnel,
etc.), by default every request's direct peer is the proxy itself — meaning
requests from any real client are classified by their network distance from
the proxy, not the client. This breaks local_bypass mode (the default Login
requirement), where local clients should skip authentication: the proxy's own
address is usually private/loopback, so every client (including public
internet) appears local and bypasses login. It also corrupts the session
cookie's Secure flag, which should reflect the real client's connection
scheme, not Collapsarr's local connection to the proxy.
Configure trusted proxies to fix this: Set COLLAPSARR_TRUSTED_PROXIES to
a comma-separated list of IP addresses or CIDR blocks identifying your
reverse proxy(ies), e.g. COLLAPSARR_TRUSTED_PROXIES=192.168.1.100 or
COLLAPSARR_TRUSTED_PROXIES=10.0.0.0/8,192.168.1.100. Once configured,
Collapsarr trusts that proxy's X-Forwarded-For and X-Forwarded-Proto
headers to classify the real client address and scheme, fixing both issues
above. An unparseable entry fails fast at startup.
Single-hop trust only: when the direct peer is on the allowlist, the
rightmost X-Forwarded-For and X-Forwarded-Proto entries (the trusted
proxy's own view of its immediate client) are used. There is no support for
multi-hop proxy chains; an install behind multiple reverse proxies must
normalize those headers before they reach Collapsarr.
If you have not configured a trusted proxy, the old workaround still
applies: use Settings → General (or the API endpoint PUT /api/settings)
to set the Login requirement to "Always required" (auth_required: "enabled")
to force authentication regardless of the apparent client address. This
runtime setting persists across restarts and applies to all subsequent
requests.
Headless deploys — seeding a credential without the setup page: a
declarative/automated deploy (Docker Compose, Ansible, etc.) has no human
available to click through /setup. Set COLLAPSARR_AUTH_USERNAME and
COLLAPSARR_AUTH_PASSWORD (together — see
Configuration) and a fresh install seeds that credential on
first boot instead — hashed before it's persisted, never stored or logged in
plaintext — and comes up already past the setup gate. COLLAPSARR_AUTH_METHOD
and COLLAPSARR_AUTH_REQUIRED are honoured at the same time if set, otherwise
the seeded credential gets the same defaults /setup would (forms,
local_bypass).
This doubles as the supported password-recovery/lockout escape hatch, but
only for a fresh or already-locked-out install with no credential
configured yet — seeding runs once and never overwrites a credential that
already exists, even if the environment variables are still set on a later
boot. It does not help recover a forgotten password once a credential
is already set; that requires clearing the existing auth_username/
auth_password_hash first (e.g. directly in the database) so the instance
has no credential again, at which point re-seeding (or /setup) applies.
Mounting Collapsarr at a subpath (COLLAPSARR_URL_BASE): if you're
routing several apps through one reverse proxy and want Collapsarr to live
under a subpath (e.g. https://example.com/collapsarr/) instead of its own
(sub)domain, set COLLAPSARR_URL_BASE=/collapsarr. Collapsarr expects the
proxy to forward the request exactly as received — no path-rewrite/strip
rule needed: it recognizes and strips its own configured prefix internally
(the same pattern ASP.NET Core's UsePathBase implements), so a plain
pass-through proxy config "just works". This is a worked example combining
it with the trusted-proxies configuration above:
# /etc/nginx/conf.d/collapsarr.conf
server {
listen 443 ssl;
server_name example.com;
location /collapsarr/ {
proxy_pass http://127.0.0.1:8282; # no path after host:port --
# nginx forwards the full
# /collapsarr/... URI unchanged,
# no rewrite rule needed
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# .env, or the compose `environment:` block
COLLAPSARR_URL_BASE=/collapsarr
COLLAPSARR_TRUSTED_PROXIES=127.0.0.1 # nginx's address -- see above
With this configuration:
- A browser request to
https://example.com/collapsarr/wantedarrives at Collapsarr as/collapsarr/wanted(nginx forwarded it unmodified); Collapsarr strips the/collapsarrprefix internally and routes it as/wanted, exactly like an unprefixed install. - A request that bypasses the proxy entirely — e.g. a Docker healthcheck
hitting
http://localhost:8282/healthdirectly — still resolves: only paths that actually start with the configured prefix are stripped; anything else passes through unchanged. - Redirects (to
/setup,/login) and the session cookie'spathattribute are both re-prefixed with/collapsarr, so the browser is sent to — and the cookie is scoped to —/collapsarr/login, never the bare/login(which the proxy isn't even routing to Collapsarr). - The served page picks up the configured prefix at runtime and prefixes its
own API calls and client-side navigation, so the UI behaves the same as an
unprefixed install — links, redirects, and API requests all resolve under
/collapsarr.
A value missing its leading slash fails fast at startup; a trailing slash is
stripped automatically. See Configuration and
docs/adr/0004-url-base-strip-middleware-not-root-path-flag.md for the
design rationale.
Development
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Run the checks
pytest
ruff check .
mypy
# Run the server (defaults to http://0.0.0.0:8282)
python -m collapsarr
# then: curl http://localhost:8282/health
# -> {"status":"ok","version":"...","warnings":[]}
# (or, if FFmpeg is missing: {"status":"degraded","version":"...",
# "warnings":[{"code":"ffmpeg_missing","message":"...","severity":"error"}]})
Configuration
All settings load from environment variables (prefixed COLLAPSARR_) with
sensible defaults, and an optional .env file is read from the working
directory. See .env.example.
| Variable | Default | Description |
|---|---|---|
COLLAPSARR_DATA_DIR |
(OS user-data dir) | Root directory for application data — the SQLite database today, logs/backups later. Defaults to platformdirs.user_data_dir("collapsarr") (e.g. ~/.local/share/collapsarr on Linux). Created automatically if missing. The Docker image sets this to /config (its mounted volume) — see Quick start. |
COLLAPSARR_DATABASE_PATH |
(derived from COLLAPSARR_DATA_DIR) |
SQLite database file path. Set this to override the location directly, independent of COLLAPSARR_DATA_DIR. |
COLLAPSARR_DATABASE_URL |
(derived from path) | Full SQLAlchemy URL override — takes precedence over both of the above. |
COLLAPSARR_HOST |
0.0.0.0 |
API server bind address. |
COLLAPSARR_PORT |
8282 |
API server bind port. |
COLLAPSARR_LOG_LEVEL |
INFO |
Log level. |
COLLAPSARR_TRUSTED_PROXIES |
(empty) | Comma-separated list of IP addresses and/or CIDR blocks (e.g. 192.168.1.100,10.0.0.0/8) identifying reverse proxies to trust for X-Forwarded-For and X-Forwarded-Proto headers. When the direct TCP peer is in this allowlist, Collapsarr uses those headers to determine the real client address and request scheme. See Reverse-proxy configuration. An unparseable entry fails fast at startup. |
COLLAPSARR_URL_BASE |
(empty) | Reverse-proxy subpath prefix (e.g. /collapsarr) for mounting Collapsarr under a subpath instead of its own (sub)domain. Empty by default — no prefix, routes serve at the root as today. The reverse proxy needs no rewrite rule; Collapsarr strips its own configured prefix internally. See Reverse-proxy configuration. A value missing its leading slash fails fast at startup; a trailing slash is stripped automatically. |
COLLAPSARR_AUTH_USERNAME |
(unset) | First-boot credential seed: UI username. Set together with COLLAPSARR_AUTH_PASSWORD — see Authentication. |
COLLAPSARR_AUTH_PASSWORD |
(unset) | First-boot credential seed: UI password. Hashed before being persisted; never stored or logged in plaintext. |
COLLAPSARR_AUTH_METHOD |
(unset — forms) |
Optional, only applied when the seed credential above is actually seeded: forms or basic. |
COLLAPSARR_AUTH_REQUIRED |
(unset — local_bypass) |
Optional, only applied when the seed credential above is actually seeded: enabled or local_bypass. |
Running on startup
Docker: the restart: unless-stopped line in the Quick start
compose file already handles this — Docker restarts the container whenever it
stops unexpectedly or the Docker daemon itself restarts. On Linux this
happens automatically on boot, since dockerd runs as a systemd service
enabled by default (systemctl is-enabled docker to confirm). On
Docker Desktop (macOS/Windows), enable Settings → General → Start Docker
Desktop when you log in so the daemon — and in turn the container — comes
up after a reboot.
Bare-metal / PyPI install: run Collapsarr as a systemd service so it
starts on boot and restarts if it crashes. Create
/etc/systemd/system/collapsarr.service:
[Unit]
Description=Collapsarr
After=network.target
[Service]
Type=simple
User=collapsarr
Group=collapsarr
WorkingDirectory=/opt/collapsarr
EnvironmentFile=/opt/collapsarr/.env
ExecStart=/opt/collapsarr/.venv/bin/collapsarr
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Adjust User/Group, WorkingDirectory, and the .venv path to match
where you installed it; EnvironmentFile should point at an .env
containing the COLLAPSARR_* variables from Configuration
(see .env.example). Then enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable --now collapsarr
Check status/logs with systemctl status collapsarr and journalctl -u collapsarr -f.
Docs
Fuller docs live on the GitHub Wiki.
License
Metadata
Release files for collapsarr 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| collapsarr-1.0.0.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| collapsarr-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.8 MB
Release files / collapsarr-1.0.0.tar.gz
| Download URL | collapsarr-1.0.0.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
800e33c9100205209c33274dd3afb093714161a228225cce56024d5bef9fee09
|
|
BLAKE2b-256 checksum How to use checksums |
352dcb71a46902e421f3e4ec5b04fc7ba16db07cd7ae8a810a79976747b58543
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 15, 2026.
Transparency logRelease files / collapsarr-1.0.0-py3-none-any.whl
| Download URL | collapsarr-1.0.0-py3-none-any.whl |
|---|---|
| Size | 650.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
413578f7ae9b2401efe7c8a9078706ccada934b2e669fb4414983c8b68090297
|
|
BLAKE2b-256 checksum How to use checksums |
51b48c5b065038a257ca26772f69fc559c41204d35931ab31873527b4ca5efda
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 15, 2026.
Transparency log