MediaManager Collapses the Arr Stack Into One Python Service
A modern selfhosted media management system for your media library
At a glance
- What is it?
- A single FastAPI application handling metadata, downloads and playback, plus the deployment choices that decide whether it fits your setup.
- Who is it for?
- MediaManager is a serious single-service option for people who find the Arr stack too spread out, and its deployment story is the strongest part of the project: one compose file, Postgres 17 with a healthcheck, images on quay.io, and migrations handled by alembic. The weak spots are equally concrete.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 79 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 9, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One service standing in for a pile of services
The Arr family of applications splits media handling into a set of cooperating daemons, each with its own container, database, queue and configuration surface. MediaManager takes the opposite position. It is a single repository, licensed AGPL-3.0, written in Python, that aims to own the whole path from metadata lookup through acquisition to organized library. At the time of this writing the repository shows 3,280 stars and 84 forks, is not archived, defaults to the master branch, and publishes a documentation site at maxdorninger.github.io rather than keeping all guidance in the README.
The README is explicit about the intent, describing the project as the modern, easy-to-use successor to the fragmented Arr stack, and it lists only three headline features: OAuth and OIDC support, TVDB and TMDB support, and a design built for Docker deployment. Those three bullets map cleanly onto what the code actually depends on. Authentication is handled by fastapi-users with the SQLAlchemy backend, plus httpx-oauth for the provider side, which is exactly what you need for an OIDC login button rather than a single hardcoded account table. Metadata comes from tmdbsimple and tvdb-v4-official, the two official client libraries for the databases the project depends on.
Acquisition is where the project reaches beyond metadata. The dependency list contains qbittorrent-api, transmission-rpc and sabnzbd-api, so the same instance can talk to a BitTorrent client, a Transmission daemon and a Usenet downloader. torf and bencoder handle torrent creation and encoding work, and pathvalidate keeps sanitized filenames from escaping their target directories. That set of clients is the practical answer to whether MediaManager can replace Sonarr and Radarr specifically: it can, at least at the level of which downloader it drives.
The dependency list is the real architecture document
Because the README keeps architecture prose to three bullets, the most informative view of the system is pyproject.toml. It requires Python 3.13 or newer, which tells you the deployment floor immediately and rules out older distributions that pin 3.11. Web handling is Starlette with FastAPI on standard, uvicorn as the server, and fastapi-restful wired in for the resource routing. Persistence is SQLAlchemy 2 with psycopg 3 in binary and pooled mode, and alembic sits next to it for schema changes, which matches the alembic/ directory and alembic.ini in the repository root.
Background work deserves attention. taskiq appears with taskiq-fastapi and taskiq-postgresql, so long running jobs are queued in Postgres rather than in a separate broker container. That is a meaningful simplification compared with a dedicated queue service, and it also means job state shares a failure domain with the database. If the Postgres container is unhealthy, both the request path and the job path stop together.
Metadata providers are abstracted behind jsonschema, cachetools and httpx, and logging uses python-json-logger with asgi-correlation-id, which is a small but telling choice: it suggests the author expects requests and background jobs to be traced across service boundaries inside one process tree. Pillow is present for image work, which lines up with a release note about poster downloads failing, and patool covers archive extraction for downloaded releases. Nothing in the list points to a frontend framework on the Python side, because the Dockerfile handles that separately.
Deployment is the supported path, and the compose file is the contract
The quick start pulls a compose file from the release bucket rather than from the repository, which makes the release artifact the thing users actually run:
wget -O docker-compose.yaml https://github.com/maxdorninger/MediaManager/releases/latest/download/docker-compose.yaml
mkdir config
wget -O ./config/config.toml https://github.com/maxdorninger/MediaManager/releases/latest/download/config.example.toml
# you probably need to edit the config.toml file in the ./config directory, for more help see the documentation
docker compose up -dThat is a five line install with two files to edit, and the tracked compose file in the repository is short enough to read in full. Two services are defined, the application and Postgres 17. The application image is pulled from quay.io/maxdorninger/mediamanager:latest, publishes port 8000, and receives CONFIG_DIR pointing at the mounted config directory. Three volumes are mounted: a data root for media, the config directory, and an images directory nested under the data mount. The healthcheck on the database uses pg_isready against the configured database and user, with a ten second interval and five retries, and the application waits for a healthy database before starting.
The health-gated dependency is the detail that matters most for a first run. If your media paths in config.toml do not line up with the volume mount comments in the compose file, the application will start cleanly and then fail on library scan, which reads as a data problem rather than a configuration problem. Match the two paths before the first start, not after.
Two descriptions of the same project, side by side
The repository carries two different self-descriptions, and neither is wrong on its own terms. The README presents a polished successor product with a logo, sponsor wall, star history chart and screenshots, and the GitHub project description calls it a modern selfhosted media management system. The packaged metadata in pyproject.toml says something else entirely: the project name is mediamanager, the version field is 0.1.0, and the description field literally reads Add your description here. Meanwhile tagged releases are numbered v1.12.1, v1.12.2 and v1.12.3. So the version string a packaging tool would see and the version a user would see in the interface are produced by different mechanisms, and the Dockerfile confirms it: the frontend stage takes a VERSION build argument and exports it as PUBLIC_VERSION, which means the displayed version is injected at image build time rather than read from pyproject.toml.
A second pair worth holding together is the compose file location. The quick start downloads docker-compose.yaml from releases/latest/download, and a compose file of the same name is also tracked in the master branch. The two can drift, and the release bucket copy is the one that matches whatever image tag the maintainer published. The v1.12.2 release notes record that images moved to quay.io in addition to GHCR because GHCR download speeds were so poor that pulling an image sometimes took fifteen minutes, and the tracked compose file already points only at quay.io. If your own saved compose file still references a GHCR path, that is a stale artifact rather than a supported configuration.
There is a smaller version of the same pattern in the developer Makefile. The help target prints a line containing $(DEV_FILE), but the file defines DC and COMPOSE and never defines DEV_FILE, so that variable expands to an empty string and the help text reads as if a file name is missing. The same help text also advertises make frontend and defines FRONTEND_SVC as frontend, which matches a separate development compose file rather than the two service quick start. The practical judgment rule is simple: trust releases/latest for what to run, trust the tracked compose file and Makefile for how the maintainer works, and check that any path you copy into your own compose file still exists in the image you pull.
Release history and the shape of maintenance
The release record is short and recent. v1.12.1 shipped on 5 January 2026 as a repair for problems introduced by v1.12.0, and its notes list pushing images to quay.io alongside fixes in MovieService. v1.12.2 followed on 8 February 2026 with what the notes call critical bug fixes, an improvement to searching torrents through Jackett, and the documentation migration to Mkdocs. v1.12.3 landed three days later on 11 February 2026, fixing a suffix formatting bug in a with_suffix call that had prevented poster images from downloading. That single call was responsible for missing artwork, which is a good illustration of how much of this project depends on third party data being shaped correctly before it reaches a filesystem.
The last recorded push to the repository is dated 22 July 2026, roughly five months after the newest release. That gap is the most useful signal for an adopter. The repository is active and not archived, but a release five months behind the branch tip means you are choosing between an image you know is published and a branch that may contain fixes nobody has cut a tag for. Sizing that trade-off against the 100 open issues is the decision, and both numbers come straight from the repository rather than from a community summary.
The project also acknowledges external backing. The README credits DigitalOcean for sponsoring the project and names one contributor image on the login screen, and the v1.12.3 notes credit a first-time contributor for the poster fix. A single maintainer with sponsor funding and outside patches is the realistic staffing model here, which sets expectations for issue response times and for how quickly a breaking config change would be communicated.
What to check before you move a library onto it
Four checks separate a smooth install from a weekend of debugging. First, pin the image tag instead of using latest, then read the compose file from the same release you pinned; a latest tag against a saved compose file is how you end up with a schema that no longer matches the migration state in the volume. Second, line up media roots: the compose file mounts ./data as /data and expects your config.toml paths to match those mount points, and any mismatch shows up as a scan that finds nothing.
Third, decide how you handle the version identity gap. Interface version and packaged version come from different places, so treat the tag and the interface display as the authoritative pair and ignore the 0.1.0 in pyproject.toml. If you script anything against this project, pin on the release tag rather than on either version field.
Fourth, look at the frontend build if you intend to run from source. The Dockerfile opens with a node 24 alpine stage, installs the web package with npm ci, and builds with PUBLIC_VERSION, PUBLIC_API_URL and BASE_PATH injected as environment variables. A branch build therefore assumes a Node 24 toolchain and a base path that matches where you serve the app from, and the compose file does not include a frontend service at all, so source builds need the development compose file the Makefile drives.
For most readers the first two checks are enough to get a running instance. The last two matter if you plan to build images, contribute patches or script the deployment, which is where the placeholder package metadata and the undefined help variable start to cost time.
Editorial conclusion
MediaManager is a serious single-service option for people who find the Arr stack too spread out, and its deployment story is the strongest part of the project: one compose file, Postgres 17 with a healthcheck, images on quay.io, and migrations handled by alembic. The weak spots are equally concrete. Package metadata still reads Add your description here at version 0.1.0 while releases run at v1.12.3, roughly 100 issues are open, the compose file you download comes from the release bucket rather than the repository branch, and the developer Makefile references a variable that is never defined. Point your config.toml at real media roots, confirm the image tag your release publishes, and check that the frontend build stage on node 24 matches your own toolchain before you commit a library to it.
Frequently asked questions
Which download clients can MediaManager talk to?
The project dependencies name qbittorrent-api, transmission-rpc and sabnzbd-api, so a single instance can drive a qBittorrent instance, a Transmission daemon and a Usenet client. torf and bencoder support torrent encoding work, and patool covers archive extraction. Release notes for v1.12.2 also describe improvements to searching torrents through Jackett, which is the indexer proxy rather than the client itself.
How do you install MediaManager?
The README quick start downloads docker-compose.yaml and config.example.toml from the latest GitHub release, creates a config directory, then runs docker compose up -d. You are expected to edit config.toml in the config directory before starting, and the tracked compose file expects a ./data mount for media plus a ./config mount, with Postgres 17 as the second service.
Why does MediaManager print a version that does not match its releases?
The version shown in the interface comes from a build argument passed into the Docker image and exported to the frontend as PUBLIC_VERSION, while pyproject.toml still carries version 0.1.0 and a placeholder description. Tagged releases are at v1.12.3. Pin on the release tag and read the interface value as the display version rather than relying on package metadata.
Does MediaManager support single sign-on?
The README lists OAuth and OIDC among its key features, and the dependency set includes fastapi-users with the SQLAlchemy backend alongside httpx-oauth for provider flows. The precise provider configuration lives in config.toml and the documentation site rather than in the repository README.
Where are MediaManager images hosted?
The tracked compose file pulls quay.io/maxdorninger/mediamanager:latest. Images are also pushed to GHCR, but release notes for v1.12.1 and v1.12.2 explain that quay.io was added because GHCR pull speeds were so slow that a download could take about fifteen minutes. A compose file still pointing at GHCR is a stale copy from before that move.
What metadata databases does MediaManager use?
TVDB and TMDB are the two listed in the README key features, and the dependency list carries tmdbsimple and tvdb-v4-official as their official clients. A separate metadata_relay directory exists in the repository tree, which suggests some upstream metadata work happens outside the main application process.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/maxdorninger-mediamanager)