Remuxarr

Remuxarr

Docker app from thetvliam's Repository

Overview

Cleans up a Sonarr/Radarr media library for better direct-play compatibility, without ever re-encoding video. Remuxes containers (MKV to MP4 when every track inside is already compatible), drops audio and subtitle tracks outside your chosen languages, and fixes broken or missing language metadata. Everything is done by lossless stream copy, so the video data itself is never touched. Integrates with Sonarr, Radarr and Plex. After first start, set your library paths and preferred languages in Settings. Dry-run mode is on by default, so the first scan shows exactly what would happen to every file before anything is changed for real.

Remuxarr

Remuxarr

Docker Pulls GHCR License: MIT

A Dockerized media-library tool for Unraid (and any Docker host) that cleans up your Sonarr/Radarr library for better playback compatibility - without ever re-encoding video.

If you've used Unmanic or similar tools and found yourself wanting less transcoding, not more, this is built specifically for that: it remuxes containers, drops audio/subtitle tracks you don't want, and fixes broken metadata - all through lossless stream copying, never touching the actual video data.


Screenshots

Screenshot Feature
Dashboard showing a file mid-process with live progress, above the pending queue Dashboard
Everything in flight and everything waiting, in one place. Files arrive from a library scan or straight from a Sonarr/Radarr webhook on import, and each job shows live progress while it runs. Dry run is on by default, so a fresh install shows you exactly what it would do to your library before it is allowed to do any of it.
Detail panel listing every track in a file and the action planned for each Detail Panel
Every track in the file - codec, language, channel layout, default and forced flags - next to exactly what is going to happen to it and why. Nothing is inferred silently: if a track is being dropped, retagged or left alone, the reason is on screen before the job runs.
Review page asking whether to remove image-based subtitles from a file Review
Genuinely ambiguous files are held for a person rather than guessed at - several undefined-language audio tracks, or image-based subtitles (PGS, VobSub) that cannot be converted to SRT. Decide once and apply it in bulk, or set a policy so the same case resolves itself from then on.
AC3 Forge listing AAC 5.1 candidate files with convert and undo actions AC3 Forge
The one deliberate exception to never re-encoding. Older AV receivers need AC3 to bitstream 5.1 over optical, so Forge finds AAC 5.1 files and converts just the audio, on files you pick. It is separate from the main pipeline, never automatic, and every conversion can be undone.
Settings page showing the language and subtitle rules for the library Settings
Which languages to keep, what to do with subtitles, which containers to prefer, and how hard to work the machine. Integrations for Sonarr, Radarr and Plex live here, as do notifications, scheduled scans, and the recycle bin that lets a processed file be put back the way it was.

Demo video

Watch the demo video — plays inline on GitHub above; this link is for viewers that strip embedded video, such as Docker Hub.


Why this exists

Most media coming out of Sonarr/Radarr carries far more than you actually need - five or six audio languages, subtitle tracks for every region, sometimes a container your TV or Plex client doesn't handle as cleanly as it could. Re-encoding to "fix" this wastes CPU, time, and quality for no benefit, since the video itself was already fine.

Remuxarr only ever touches what's cheap and lossless to touch:

  • Container remuxing - MKV → MP4 when every track inside is already MP4-compatible, so no video re-encode is ever needed for this.
  • Track filtering - drops audio/subtitle tracks outside your kept languages, using stream copy, never transcoding anything to do it.
  • Metadata correction - fixes missing (und) language tags, and can correct wrong ones too (e.g. a track mistagged in a completely different language than what's actually spoken). Audio and subtitles are controlled separately, so you can tag undefined audio automatically while holding undefined subtitles for a decision, or the reverse (Settings → Library & Processing → Metadata).

Nothing is ever re-encoded - not video, not audio, under any setting, for any reason. That's not a preference here - it's the entire point of the project. (If you specifically need AAC 5.1 → AC3 for an older AV receiver's bitstream passthrough, that's a deliberate, reviewable, undoable operation in AC3 Forge - never something the main pipeline does automatically to every matching file.)

This application was generated entirely using Claude. I acted as the architect, prompt engineer, and primary tester to solve a specific problem for myself, and wanted to share the functional result with the open-source community!

Features

  • Automatic library scanning - full probe on first sight, fast delta (size/mtime) scans afterward.
  • Sonarr / Radarr webhook integration - reacts to imports and upgrades directly, with automatic path translation between how each *arr sees files and how Remuxarr does.
  • Plex integration - refreshes affected library sections after every job. A separate, opt-in backlog can additionally verify Plex's own metadata and force an explicit re-analyze on the rare files Plex's own maintenance misses - most installs won't need this turned on; see Settings → Integrations → Plex Analyze Backlog for why.
  • AC3 Forge - AAC 5.1 → AC3 for older receivers, on files you pick, with an undo. Separate from the main pipeline and never automatic; see Screenshots above.
  • Manual review, with bulk resolution - ambiguous files are held for a human decision rather than guessed at, resolvable in bulk or by a standing policy (Settings → Library & Processing → Subtitles); see Screenshots above.
  • Audio Language Review - search-and-bulk-correct tool for tracks that have a wrong language tag rather than a missing one (common with some release groups) - confirm it's actually correct, or apply the right tag to every matching file at once.
  • Dry run mode - see every planned action across your whole library before anything real is touched. On by default for a fresh install - see First-time configuration below.
  • Revert to original (beta) - keeps the audio and subtitle tracks a job removed, so a processed file can be put back exactly as it was. Off by default and needs a volume mounted; see Reverting a processed file below.
  • Email notifications - on job failure, with a circuit breaker so a bad setting doesn't flood your inbox.
  • Scheduled scans, manual and orphaned-file cleanup, abort/pause controls, and a live log viewer, all from the web UI.

Installation

Prebuilt images are published to both the GitHub Container Registry (GHCR) and Docker Hub - you do not need to clone this repository to run Remuxarr. The two are identical, built from the same commit in the same workflow; use whichever your setup prefers. Unraid's Community Apps and template ecosystem lean toward Docker Hub, so that's the one to use there unless you have a reason not to.

Branch Docker Hub GHCR
Main (stable) thetvliam/remuxarr:latest ghcr.io/thetvliam/remuxarr:latest
Testing (beta) thetvliam/remuxarr:testing ghcr.io/thetvliam/remuxarr:testing

Every build also publishes an immutable :<short-commit-sha> tag (e.g. :37a7265) to both registries, so you can pin to an exact build and roll back if a latest/testing update ever misbehaves.

Unraid

See UNRAID_DEPLOYMENT.md for a step-by-step GUI walkthrough. Use thetvliam/remuxarr:latest as the Repository value in the template.

Docker Compose

Create a docker-compose.yml anywhere on your Docker host:

services:
  remuxarr:
    image: thetvliam/remuxarr:latest   # :testing for the beta branch, or ghcr.io/thetvliam/remuxarr:latest
    container_name: remuxarr
    restart: unless-stopped

    ports:
      - "9191:9191"          # Web UI + API

    volumes:
      # Settings and database. Map this to a host path that survives
      # container updates - everything you configure lives here.
      - /path/to/appdata/remuxarr/config:/config

      # Your media library. Mount it at the same paths Sonarr/Radarr use
      # inside their own containers where you can - it makes the path
      # translation in Settings simpler (often unnecessary entirely).
      - /path/to/your/movies:/media/movies
      - /path/to/your/tv:/media/tv

      # Optional: recycle bin for "revert to original". Stores the audio
      # and subtitle tracks a job removed - never the video - so a file
      # can be put back the way it was. Leave it out and the feature
      # stays off. Must survive restarts, so not under /tmp.
      - /path/to/appdata/remuxarr/recycle:/recycle

    environment:
      - TZ=America/New_York   # see "Setting your time zone" below

    # Optional: stage FFmpeg's temp output in RAM instead of on the array.
    # Remuxarr checks free space first and falls back to the output file's
    # own directory when a file is too large for it, so this is safe to
    # leave enabled - and safe to delete if you'd rather not use RAM.
    tmpfs:
      - /tmp/remuxarr

Then:

  1. Change the volume paths (/path/to/...) to match your host.

  2. Start it:

    docker compose up -d
    
  3. Open http://<your-host-ip>:9191.

Setting your time zone

TZ sets the container's own clock, and that clock is what decides when scheduled work runs: the scan times under Settings → Maintenance & Logs → Scheduled Scans, and the analyze window under Settings → Integrations → Plex Analyze Backlog (02:00-06:00 by default). Leave TZ unset and the container runs on UTC, so a window you meant for the small hours lands wherever UTC puts it - 02:00-06:00 UTC is 22:00-02:00 in New York and the middle of the afternoon in Sydney.

The timestamps in the log viewer and in job history do not depend on this. Those are stored in UTC and converted by your browser, so they already read correctly in your own zone whether or not TZ is set. That is worth knowing because it removes the symptom you would otherwise notice: every clock in the UI looks right, and the only sign anything is wrong is a scan starting at the wrong end of the evening.

It takes an IANA time zone name, which is an Area/City pair - not a country name, not an abbreviation like EST, and not an offset like GMT+1. Pick the listed city closest to you within your own country; large countries have several, because their regions follow different daylight-saving rules.

Country Example values
United States America/New_York, America/Chicago, America/Denver, America/Phoenix (no DST), America/Los_Angeles
Canada America/Toronto, America/Winnipeg, America/Edmonton, America/Vancouver, America/Halifax
United Kingdom Europe/London
Ireland Europe/Dublin
Germany Europe/Berlin
France Europe/Paris
Netherlands Europe/Amsterdam
Spain Europe/Madrid
Australia Australia/Sydney, Australia/Adelaide, Australia/Brisbane (no DST), Australia/Perth
New Zealand Pacific/Auckland
Japan Asia/Tokyo
India Asia/Kolkata
Singapore Asia/Singapore
Brazil America/Sao_Paulo
South Africa Africa/Johannesburg
No local time Etc/UTC

A few things that trip people up:

  • The city is a label, not a location requirement. Europe/London is correct for all of Scotland, Wales, and Northern Ireland too - the city just names the rule set that region follows.
  • Spelling is exact. Names are case-sensitive and use underscores, so America/New_York works and america/new_york or America/New York do not.
  • Daylight saving is automatic. That is the whole reason to use a zone name instead of a fixed offset - the clock shifts itself on the right dates.
  • Avoid Etc/GMT±N values. Their signs are inverted from what you would expect (Etc/GMT+5 is actually UTC−5), so they are an easy way to end up an hour or ten wrong.

To find your own, run timedatectl list-timezones on a Linux host (or check Unraid's Settings → Date & Time, which already displays it). After starting the container, docker exec remuxarr date confirms the setting took effect - and that is the check worth doing, since nothing in the web UI will look wrong if you get it incorrect.

On Unraid, set this in the container's TZ variable rather than here; Unraid's own Date & Time page governs the host, not the container, so the two are separate settings. See UNRAID_DEPLOYMENT.md.

SELinux hosts (Fedora, RHEL, some Synology setups): append :Z to the /config mount and :z to each media mount - the media ones are shared with your Sonarr/Radarr/Plex containers, so they must use the lowercase shared label. Both are harmless no-ops on non-SELinux hosts like stock Unraid. See this repo's own docker-compose.yml for a fully annotated example.

Building from source

Only needed if you want to modify the code:

git clone https://github.com/thetvliam/remuxarr.git
cd remuxarr
cp .env.example .env      # required — the bundled compose file reads it
docker compose up -d --build

Every value in .env.example already matches the app's own built-in default, so an untouched copy is fine; edit it only to override something specific (the file's own comments explain each one). The first build takes a few minutes - it installs dependencies, builds the frontend, and fetches FFmpeg. Subsequent starts are fast.

The bundled docker-compose.yml builds the image locally rather than pulling it, and is also the fully annotated reference for the SELinux labels and tmpfs staging mentioned above.

First-time configuration

Everything from here happens in the web UI, not in any config file:

  1. Go to Settings → Library & Processing → Library and set your scan paths - this is empty on a fresh install, deliberately, so nothing happens until you point it at your actual library. Use the container-side paths (e.g. /media/movies, /media/tv), not your host paths.
  2. If you keep audio/subtitles in a language other than English, set that in Settings → Library & Processing → Audio / Subtitles - both default to English.
  3. Trigger a scan. Dry run is on by default - this first scan shows you exactly what would happen to every file, without touching anything.
  4. Review the Dry Run tab. Once the planned actions look right, turn dry run off in Settings → Worker - real processing begins from here.
  5. Auto-start is on by default, meaning the queue processes itself once dry run is off. If you'd rather review the queue manually before anything runs, turn this off in Settings → Worker.
  6. Sonarr, Radarr, Plex, and email integrations are all off until you provide real connection details - nothing is assumed enabled.

Reverting a processed file (beta)

Remuxarr can keep whatever a job removed, so a file can be put back the way it was. It is off by default and needs the /recycle volume mounted - see Installation above.

This feature is in beta. It is newer than the rest of Remuxarr and has had less time in front of real libraries. Nothing it does is destructive on its own - it only ever adds a copy of what a job removed - but treat a revert point as a convenience rather than a backup, and do not rely on it as your only route back.

It exists for the period while you are still working out what your language and subtitle rules should be, which is exactly when a rule turns out to be wrong on a few hundred files. It is not meant to be a permanent archive, and the retention limits are set accordingly.

What is kept. Only the tracks a job actually removed - never the video. A job that drops three foreign-language audio tracks and twenty subtitles stores those and nothing else, so what it costs is a fraction of the file rather than a second copy. A job that removes nothing stores nothing.

What it costs. Bounded twice, in Settings → Recycle Bin: 7 days and 20GB by default. Both apply - whichever is reached first. Set either to 0 to disable that limit.

Reverting. Each entry in Settings → Recycle Bin restores the file to its exact original state: every track back, in the original order, with the original language tags, titles, default/forced flags and attachments, in the original container. If the job converted MKV to MP4, reverting converts it back and restores the original filename.

When it will refuse. A revert point records the file as the job left it. If something else has written to that file since - Sonarr upgrading the episode is the usual case - the stored tracks belong to a different release, and muxing them in would produce a file that plays and is quietly wrong. Those entries stay listed with the reason shown, so you can discard them, but they will not offer to revert.

If a file is renamed, Remuxarr loses track of which file the entry belongs to - a rename looks the same as a deletion from the outside. The entry moves to Unmatched rather than being thrown away, and can be matched back: a renamed file is byte-for-byte identical, so it is identified by its fingerprint rather than guessed at.

Extracted subtitle files are removed again when you revert, since their content goes back inside the file - but only the ones that job created. If you already had a .srt there (from Bazarr, say) it is left alone, as is one you have edited since.

Two limitations worth knowing. A job whose only casualty is an attachment - a font, say - stores nothing, because Matroska cannot hold a file with no tracks. And Matroska cover art comes back as a still-image video track rather than an attachment, with its filename and mimetype intact; nothing is lost, but a player may list it as a second video stream.

Development

The backend has a real test suite covering the decision engine (what happens to each file and why), library scanning and deletion cascades, queue and job lifecycle, job finalisation, Sonarr/Radarr webhook path translation and notification, FFmpeg command construction, AC3 Forge, the scheduler and Plex client, settings persistence, backup/restore, startup recovery, revert-to-original (including real-FFmpeg round trips that capture from a file and restore it, comparing stream by stream), and a sample-library regression suite that runs the real pipeline against a fixed set of probed media files:

pip install -r requirements.txt -r tests/requirements-test.txt
pytest

The frontend has its own suite covering the app's central state hook (routing, toasts, history invalidation), every mutating user action, paginated and history data fetching, the settings load and save paths, integer input handling, the shared formatters that decide how a size, a duration and a UTC timestamp read, the theme serialiser that round-trips a theme between its source text and the object the app renders from, and the developer theme editor's preview isolation, control coverage and export, and the invariants every shipped theme must satisfy:

cd frontend && npm install && npm test

Both run in CI on every push, alongside pyflakes and eslint. A few backend tests exercise a real FFmpeg/ffprobe and skip if the binaries are missing - CI installs them so they always run.

See tests/README.md for more detail, including how to run this same suite inside a deployed container against production FFmpeg.

License

MIT - see LICENSE.

Media gallery

1 / 5

Install Remuxarr on Unraid in a few clicks.

Find Remuxarr in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Remuxarr Review the template variables and paths Click Install

Download Statistics

9,197
Total Downloads

Related apps

Details

Repository
thetvliam/remuxarr:latest
Last Updated2026-08-23
First Seen2026-07-28

Runtime arguments

Web UI
http://[IP]:[PORT:9191]
Network
bridge
Shell
bash
Privileged
false

Template configuration

WebUIPorttcp

HTTP port for the web UI and API.

Target
9191
Default
9191
ConfigPathrw

Persistent settings and database. Must survive container updates.

Target
/config
Default
/mnt/user/appdata/remuxarr/config
MoviesPathrw

Your movies library.

Target
/media/movies
Default
/mnt/user/media/movies
TV ShowsPathrw

Your TV library.

Target
/media/tv
Default
/mnt/user/media/tv
TempPathrw

FFmpeg staging directory. Keep under /tmp so it stays RAM-backed.

Target
/tmp/remuxarr
Default
/tmp/remuxarr-temp
Recycle BinPathrw

Holds revert-to-original data: the audio and subtitle tracks removed by a job, so files can be put back. Leave empty to disable. Must NOT be under /tmp — that is RAM-backed and would be lost on reboot.

Target
/recycle
Default
/mnt/user/appdata/remuxarr/recycle
TZVariable

Your IANA time zone, e.g. Europe/London or America/New_York. Leave empty and the container runs on UTC, which means scheduled scans and the Plex analyze window fire on UTC rather than your local time. Timestamps shown in the web UI are converted by your browser and are correct either way.