Self-hosted service
getmydia/mydia avatar
getmydia/mydia

Mydia: a self hosted media manager that shows its release ranking reasoning

Your personal media companion, built with Phoenix LiveView

800 stars21 forksElixirAGPL-3.0

At a glance

What is it?
An Elixir and Phoenix LiveView platform for tracking, organising and monitoring movies and TV shows, with an unusually explicit rule set for choosing which release to grab, a player that streams over an encrypted peer-to-peer connection, and an AGPL-3.0 licence on a 0.x codebase.
Who is it for?
Adopt Mydia if you want a self hosted media library with explicit, inspectable release selection rules, multi user roles and a player that needs no port forwarding. Do not adopt it for a production library yet, because the project warns it is in early development at 0.x with breaking changes expected, and check the AGPL-3.0 terms in the LICENSE file before you deploy it on infrastructure that others can reach.
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 3 days ago.
What is it written in?
Mainly Elixir, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The release ranking is the product, not the interface

Most media managers treat picking a release as an implementation detail. Mydia makes it the centre of the design, and the documentation explains the rule set in enough detail that you could argue with it.

Automatic and manual search rank releases the same way. Automatic search grabs the top one; manual search shows you the list in that order. That is a meaningful design choice, because it means the manual result is not a separate code path with different rules, so what you see in the manual list is what the automated grab would have taken.

Before ranking, releases are dropped. The documented exclusions are fake or malformed release names, a different title than the one searched, tags you blocked, custom formats your profile rejects, sources your profile excludes such as cam rips, resolutions below your profile's minimum, and Usenet posts too new to be complete. The asterisked items are not removed in manual search, they are kept at the bottom of the list instead.

Ranking then proceeds in steps, each one only breaking ties left by the step above: right season and episode first, then preferred audio language, then resolution in your profile's order with unlisted resolutions last, then the number of preferred audio languages, then the custom format score, and finally the quality score. That last score is roughly 60 percent profile fit across resolution, codec, audio, source, size and HDR, plus seeders or Usenet completeness, plus name match, with torrents at zero seeders losing 30 percent.

The upgrades path follows the same ranking and adds two conditions: the candidate must beat your current file by the profile's upgrade margin or add a preferred audio language, and it is checked twice, once on the release before download and once on the real file after. Your old file is kept until the new one passes. That is a careful answer to the most common way an automated upgrade goes wrong.

One interface for TMDB metadata, indexers and download clients

The feature list is broad, and the reason it works is that the pieces are deliberately decoupled.

Metadata comes from TMDB and TVDB for both movies and TV shows. Download clients are separate integrations: qBittorrent, Transmission, rqbit for torrents, SABnzbd and NZBGet for Usenet, plus debrid providers. Indexers are reached through Prowlarr, Jackett, or a built-in Cardigann implementation that the documentation marks as experimental.

So the architecture is the familiar one: your indexer exposes a search API, Mydia queries it, ranks what comes back, and hands the winner to a download client you already run.

On top of that sit the access features. There is multi user support with admin and guest roles and a request workflow, so a guest can ask for something rather than pulling anything directly. There is SSO with local authentication plus OIDC and OpenID Connect. Import lists sync from TMDB watchlists, popular and trending, marked experimental.

The interface itself is Phoenix LiveView, which is why the README lists the real-time UI as a feature rather than a detail. LiveView keeps state on the server and pushes updates over a websocket, so a page that shows download progress, indexer status and library changes does not need polling, and the same codebase renders the whole experience without a separate front end framework.

Getting it running with Docker Compose and two generated secrets

The quick start is a compose file and two secret keys.

The keys are generated with openssl, once for the Phoenix secret key base and once for the Guardian secret:

bash
openssl rand -base64 48  # SECRET_KEY_BASE
openssl rand -base64 48  # GUARDIAN_SECRET_KEY

Those values go into the compose file along with the paths to your media:

yaml
services:
  mydia:
    image: ghcr.io/getmydia/mydia:latest
    container_name: mydia
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/New_York
      - SECRET_KEY_BASE=your-secret-key-base-here
      - GUARDIAN_SECRET_KEY=your-guardian-secret-key-here
      - PHX_HOST=localhost
      - MOVIES_PATH=/media/library/movies
      - TV_PATH=/media/library/tv
    volumes:
      - ./config:/config
      - /path/to/media:/media
    ports:
      - 4000:4000
    restart: unless-stopped

Then start it:

bash
docker compose up -d

and open http://localhost:4000 to create your admin account.

Two things stand out. The PUID and PGID variables mean the container runs as your own user rather than root, which is what you want when it writes into a media library. And the config directory is a volume, because the SQLite database lives inside it by default, at `/config/mydia.db` in Docker.

The environment example file spells out the precedence rule: environment variables win over `config/config.yml`, which wins over the application defaults. The same file documents the settings you are most likely to need, including the port and host interface, the public URL scheme and hostname, the database path, the pool size, and a set of SQLite tuning knobs for cache size, busy timeout, journal mode and synchronous mode.

Mydia Player reaches your library without opening a port

The player is the part that goes beyond the usual server side media manager. It is described as a cross-platform app that streams your library from anywhere over an encrypted peer-to-peer connection, with no port forwarding and no VPN.

That design choice removes the classic exposure problem. A conventional self hosted media server is reachable from outside only if you forward a port, which means putting an authentication layer in front of it and hoping the application is sound. A peer-to-peer connection inverts the model, with the devices finding each other rather than the server being published.

The player also browses and plays from Plex, Jellyfin and Stash servers, and the documentation marks that part as experimental with a link to the guide for connecting other servers.

Platform coverage is wide. There is an Android APK with install-from-unknown-sources enabled that updates itself afterwards, an iOS build through TestFlight, a notarised macOS disk image, a Windows per-user install offered both as a PowerShell one-liner and as a downloaded installer, a Linux build as Flatpak or tarball with Flatpak recommended, and a web player served by your own Mydia server at `/player` with nothing to install at all.

The Windows entry is worth a note because it documents a deliberate trade-off: the PowerShell command avoids the SmartScreen prompt that the unsigned installer receives, at the cost of the user running a remote script. That is the kind of honest packaging detail that tells you the project has thought about what a non technical user sees.

The repository is a much larger system than the README shows

The top level listing of the repository is long, and it explains the scale of what you are installing.

There is a `player/` directory, which is the Flutter application, and the Dockerfile makes the reasoning visible: the build installs the Flutter SDK itself from a Debian bookworm slim base rather than using a published Flutter image, because the comments say those images lag patch releases, and it reads the version pin from `player/.fvmrc` so the Docker layer is keyed on that file alone. The same file notes that a CI job scans every build file for a Flutter version literal and fails the build on a hit, so the pin has exactly one source.

There is a `metadata-relay/` directory, which corresponds to the `METADATA_RELAY_URL` setting in the environment example, described there as the URL for the self-hosted metadata relay service with a default of https://relay.mydia.dev. There are `plugins/` and `plugins-extra/` directories, `native/` with a Rust toolchain file, `bench/` for benchmarks, `infra/`, `hooks/`, `nix/`, `patches/`, `config/`, `docs/`, `priv/` and `server/`.

The tooling is unusually complete for an application project. There is a `mix.exs` and `mix.lock` for the Elixir side, a `flake.nix` and `devenv.nix` with a `devenv.lock` for reproducible development environments, an `.otp-version` and an `.elixir-version`, a `.credo.exs` with a `.credo.ratchet.exs` that tightens over time, a `.formatter.exs`, and multiple compose files for different purposes, including `compose.test.yml`, `compose.windows.yml` and `compose.player-e2e.yml`.

There are also agent instruction files in the repository, `AGENTS.md`, `CLAUDE.md` and `GEMINI.md`, plus a `CONCEPTS.md`. For a project of this size that is a reasonable sign that the contributors expect substantial contributions.

Where Mydia is the wrong choice

Three boundaries are worth being clear about.

The first is maturity. The README carries a warning that Mydia is in early development at 0.x.x and that breaking changes should be expected, and the current tags are v0.17.0-beta.1, beta.2 and beta.3, all from October 2026. Three beta tags in a week for a 0.17 release is an active pre-release line, and every current release is a 0.17.0 beta pre-release, so there is no stable artefact to pin to.

The second is that some of the interesting parts are explicitly experimental. Import lists, the built-in Cardigann indexer, and connecting the player to Plex, Jellyfin or Stash are all marked experimental in the documentation. Experimental features in a 0.x project tend to change shape, so if the import list is the reason you are considering Mydia, that is the part most likely to move.

The third is the boundary of the design. Mydia is a media manager with acquisition built in. It talks to indexers and hands releases to download clients you run, and it manages a library it tracks and monitors. If what you want is a read only companion over an existing Plex or Jellyfin library, with no search or download side at all, then the player alone is the part of this project that fits, and the server is more than you asked for.

The alternative to name is one of the established media managers, which differ in approach by making acquisition the primary function too but with a longer public release history and a different configuration model. The difference in approach here is that Mydia exposes its release selection rules as a documented, tiered decision you can read and reason about, rather than as a settings screen, which is the more interesting proposition and also the one carrying the most configuration surface.

Editorial conclusion

Adopt Mydia if you want a self hosted media library with explicit, inspectable release selection rules, multi user roles and a player that needs no port forwarding. Do not adopt it for a production library yet, because the project warns it is in early development at 0.x with breaking changes expected, and check the AGPL-3.0 terms in the LICENSE file before you deploy it on infrastructure that others can reach. Verify first which download clients and indexers you actually run, since the integration list is long but not uniform, and confirm the release ranking rules match your own before you let it fetch anything.

Frequently asked questions

How does Mydia choose which release to download?

Automatic and manual search rank releases the same way, with automatic taking the top result. Releases are dropped for fake names, wrong titles, blocked tags, rejected custom formats, excluded sources such as cam rips, resolutions below the profile minimum, and incomplete Usenet posts, and the rest are ranked by episode, audio language, resolution, language count, custom format score and a quality score weighted roughly 60 percent profile fit.

How do I install Mydia with Docker Compose?

Generate SECRET_KEY_BASE and GUARDIAN_SECRET_KEY with openssl rand -base64 48, put them and your media paths into a docker-compose.yml using the ghcr.io/getmydia/mydia:latest image with port 4000 published, then run `docker compose up -d` and open http://localhost:4000 to create the admin account.

Which download clients and indexers does Mydia support?

Download clients are qBittorrent, Transmission, rqbit, SABnzbd, NZBGet and debrid providers. Indexers are reached through Prowlarr, Jackett, or a built-in Cardigann implementation that the documentation marks as experimental.

How does the Mydia Player connect to my server?

It streams over an encrypted peer-to-peer connection, so no port forwarding and no VPN are needed. It is available as an Android APK, an iOS build through TestFlight, a macOS disk image, a Windows per-user install, a Linux Flatpak or tarball, and a web player served at /player by your own server.

Is Mydia safe to deploy for a production media library?

The project itself warns that it is in early development at 0.x.x and that breaking changes should be expected, and the recent tags are v0.17.0 beta releases. The licence is AGPL-3.0 with the terms in the LICENSE file.

Official sources

  1. getmydia/mydia on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/getmydia-mydia.svg)](https://hysenlabs.com/projects/getmydia-mydia)

Was this useful?