New: the Homelab Planning Bundle. Both planners together for $19.99 instead of $27.98Save $7.99GET THE BUNDLE

Homelab Addiction
Self-Hosting

Self-Hosted Media Stack on Docker: Jellyfin, Sonarr, Radarr, and Request Flow

Build a Docker media stack by aligning container roles, shared paths, permissions, and request flow before adding optional services.

HomelabAddiction Research Desk11 min read

Disclosure: This article contains affiliate links where noted. Recommendations are matched to the documented use case and are not presented as first-hand ownership or testing.

Documentation basis: This guide is grounded in Jellyfin installation documentation; Servarr Docker Guide; Seerr documentation. It explains documented behavior and planning trade-offs rather than claiming private testing.

Build a Docker media stack by aligning container roles, shared paths, permissions, and request flow before adding optional services.

The workflow below turns the choice into explicit roles, boundaries, and review points so the resulting setup stays understandable when services or hardware change.

Evidence-led decision flow for 111

Key Takeaways

  • Start with the smallest design that matches the workload.
  • Record dependencies, ownership, and recovery assumptions before adding complexity.
  • Use official documentation as the source of truth for version-specific behavior.

Key Takeaways

  • Start with the simplest architecture that satisfies the service requirement.
  • Match paths, permissions, network boundaries, and dependencies to the documented operating model.
  • Verify the result from the host and from the client path that matters before expanding the stack.
What each app does

Here is the beginner version, in plain English.

Jellyfin

Jellyfin is your media server. It scans folders, pulls in metadata, and gives you a clean interface for streaming across your devices.

Sonarr

Sonarr manages TV libraries. It tracks show structure, seasons, and episode naming.

Radarr

Radarr does the same job for movies.

Bazarr

Bazarr handles subtitles and keeps them linked to your media library.

Jellyseerr

Jellyseerr gives you a request portal, which is much nicer than giving family members direct access to Sonarr and Radarr.

Optional services

You will see people add tools like Prowlarr, Tautulli, Tdarr, reverse proxies, SSO, and GPU-optimized transcoding stacks. Those can be useful, but they are not day-one requirements for a beginner build.

What you will need

For a simple starter setup, you need:

  1. A Linux host with Docker and Docker Compose installed
  2. At least 8 GB RAM if you want the stack to feel comfortable
  3. Enough storage for config files and your media library
  4. A user account with permission to access your media directories
  5. Local network access to the server while you do the first setup

A mini PC is plenty for many starter homelabs. If you are shopping for gear, these are the three products the guide would look at first:

The folder structure that saves you from future pain

This is the part many guides rush through, and it is the part that causes the most frustration later.

transmission signup

article_topic // Self-Hosted Media Stack on Docker

Don't leave without the setup notes.

Get practical homelab guides, failure logs, and beginner-friendly build notes in your inbox.

Enter your email address to receive the Homelab Addiction newsletter.

Weekly only. No spam. Unsubscribe anytime.

Why this matters: Sonarr, Radarr, and any download client all need to agree on where files live. If one container sees /downloads and another sees a completely different mount that only looks similar from the host side, imports become copy operations, hardlinks fail, and storage use balloons.

The easiest mental model is this: every app that touches media should be looking at the same filing cabinet, not its own private copy of the cabinet.

We will use this host layout:

mkdir -p ~/media-stack/{jellyfin,sonarr,radarr,bazarr,jellyseerr,qbittorrent}/config
mkdir -p /srv/media/{movies,tv,downloads}

Then make sure your media user owns those directories:

sudo chown -R $USER:$USER ~/media-stack
sudo chown -R $USER:$USER /srv/media

If you prefer to run the containers with a specific UID and GID, collect them now:

id

You will see output like this:

uid=1000(akash) gid=1000(akash) groups=1000(akash),27(sudo)

Write down your uid and gid. We will use them in the Compose file.

Step 1: Create the Docker Compose file

Why this matters: Docker Compose gives you a single source of truth. If the host dies, if you migrate to a new machine, or if you need to rebuild after a bad update, you have one file describing the stack.

Create ~/media-stack/docker-compose.yml:

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
    ports:
      - "8096:8096"
    volumes:
      - ./jellyfin/config:/config
      - /srv/media:/media
    restart: unless-stopped

  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
    ports:
      - "8989:8989"
    volumes:
      - ./sonarr/config:/config
      - /srv/media/tv:/tv
      - /srv/media/downloads:/downloads
    restart: unless-stopped

  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
    ports:
      - "7878:7878"
    volumes:
      - ./radarr/config:/config
      - /srv/media/movies:/movies
      - /srv/media/downloads:/downloads
    restart: unless-stopped

  bazarr:
    image: lscr.io/linuxserver/bazarr:latest
    container_name: bazarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
    ports:
      - "6767:6767"
    volumes:
      - ./bazarr/config:/config
      - /srv/media/movies:/movies
      - /srv/media/tv:/tv
    restart: unless-stopped

  jellyseerr:
    image: fallenbagel/jellyseerr:latest
    container_name: jellyseerr
    environment:
      - LOG_LEVEL=info
      - TZ=UTC
    ports:
      - "5055:5055"
    volumes:
      - ./jellyseerr/config:/app/config
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
      - WEBUI_PORT=8080
    ports:
      - "8080:8080"
      - "6881:6881"
      - "6881:6881/udp"
    volumes:
      - ./qbittorrent/config:/config
      - /srv/media/downloads:/downloads
    restart: unless-stopped

If your id output showed a UID or GID other than 1000, replace those values before starting the stack.

What could go wrong

If you skip the UID and GID check, the apps might start but fail to write metadata, rename files, or import completed downloads. That is one of the most common beginner mistakes in self-hosted media stacks.

Step 2: Start the stack

Why this matters: launching everything at once lets you verify that your networking, ports, and volumes are at least valid before you start app-specific configuration.

From ~/media-stack run:

docker compose pull
docker compose up -d

Check status:

docker compose ps

You want to see containers in an Up state.

If one crashes immediately, inspect logs:

docker compose logs --tail=100 sonarr
docker compose logs --tail=100 radarr
docker compose logs --tail=100 jellyfin

Step 3: Open each service locally

Use your server IP or hostname on your LAN:

  • Jellyfin: http://your-server:8096
  • Sonarr: http://your-server:8989
  • Radarr: http://your-server:7878
  • Bazarr: http://your-server:6767
  • Jellyseerr: http://your-server:5055
  • qBittorrent: http://your-server:8080

Why this matters: local-first setup keeps your blast radius small. If something is misconfigured, you fix it before introducing HTTPS, reverse proxies, or internet exposure.

If remote access is part of your plan, come back to it after the stack works locally. Two HomelabAddiction guides worth reading next are How to Set Up Nginx Proxy Manager on Docker and How to Set Up HTTPS for Your Homelab.

Step 4: Set up Jellyfin first

Why this matters: Jellyfin is where you can see progress. It gives you a visible result quickly, which is good for motivation and good for troubleshooting.

In Jellyfin:

  1. Create your admin account.
  2. Add a Movies library pointed at /media/movies.
  3. Add a TV Shows library pointed at /media/tv.
  4. Let the first metadata scan finish.

If those folders are empty right now, that is fine. We are validating the structure.

Step 5: Configure Sonarr and Radarr

Why this matters: these apps are only helpful when they know where your media should end up. Think of them as organizers with rules. If the destination shelves are wrong, the whole system feels broken even when the containers themselves are healthy.

In Sonarr

  1. Go to Settings -> Media Management and enable rename options you actually want.
  2. Add a root folder: /tv
  3. Under Settings -> General, copy your API key somewhere safe.

In Radarr

  1. Go to Settings -> Media Management
  2. Add a root folder: /movies
  3. Under Settings -> General, copy the API key.

Optional download client wiring

If you use a compatible client for legal downloads, connect it under Settings -> Download Clients in Sonarr and Radarr.

For the qBittorrent container in this guide, the default path for completed downloads inside the app should be /downloads.

This is also where many people get stuck. If Sonarr or Radarr says a download finished but cannot import it, the usual cause is not magic. It is a path mismatch.

Step 6: Configure Bazarr and Jellyseerr

Why this matters: these are quality-of-life services, but they are the kind that make the stack feel polished instead of homemade.

Bazarr

  1. Connect Bazarr to Sonarr and Radarr using their API keys.
  2. Set your preferred subtitle languages.
  3. Run a test sync.

Jellyseerr

  1. Connect Jellyseerr to Jellyfin.
  2. Connect it to Sonarr and Radarr using their URLs and API keys.
  3. Set the default movie and TV destinations.

Now you have a cleaner request layer for your household instead of making everyone learn the admin apps.

From the lab

Step 7: Add a few guardrails before you expose anything

Why this matters: media stacks are fun, and fun projects make people rush. The most common version of that rush is exposing services to the internet before passwords, HTTPS, or access boundaries are in place.

Before opening this stack beyond your LAN, do these three things:

  1. Use strong unique passwords for every admin account.
  2. Put the stack behind a reverse proxy with TLS.
  3. Add a real authentication layer for sensitive dashboards.

If you want a beginner-friendly auth path, read How to Set Up Authentik for Your Homelab. If you want to improve the Compose side of the stack itself, also read Docker Compose Best Practices in 2026.

Common mistakes

1. Using different paths inside different containers

This is the classic mistake. qBittorrent sees /downloads, but Sonarr sees /data/downloads, and now imports fail. Keep the path mapping simple and consistent.

2. Ignoring permissions

If your host user owns /srv/media but the containers run with a different UID and GID, file moves and metadata writes can fail quietly.

3. Starting with too many services

A twelve-container stack looks impressive, but it gives beginners twelve places to get confused. Start with the core, then add optional services after the basics work.

4. Exposing admin panels too early

Do not forward ports to the internet just because the UI loads locally. Get HTTPS and authentication right first.

5. Skipping backups for config data

Your media files matter, but your app databases and config directories matter too. Back up ~/media-stack along with your library metadata.

A simple upgrade path once the basics work

One reason people get overwhelmed by self-hosted media management is that they think they need the final form on day one. You do not.

A better path looks like this:

  1. Stage 1 - Jellyfin + folder layout
  2. Stage 2 - Add Sonarr and Radarr
  3. Stage 3 - Add Bazarr and Jellyseerr
  4. Stage 4 - Add reverse proxy, HTTPS, and SSO
  5. Stage 5 - Add monitoring, backups, and hardware acceleration tuning

That staged path matters because it gives you checkpoints. Each stage should feel stable before the next one begins.

Why shared paths are such a big deal

This deserves one more plain-English explanation because it is the single most useful concept in this whole guide.

Imagine your download client places a file into a basket labeled downloads. Sonarr or Radarr then needs to take that file and place it on the correct library shelf. If both apps are looking at the same room, that move is fast and predictable. If they are looking at different rooms that only happen to have similar labels, one app says, “I know the file is there,” while the other says, “I cannot see it.”

That is why you will hear experienced self-hosters talk so much about shared paths and hardlinks. It is not because they enjoy jargon. It is because folder mapping is the difference between a stack that feels smooth and a stack that feels cursed.

FAQ

Do I need all five apps to get started?

No. You can absolutely start with Jellyfin alone, then add Sonarr and Radarr once your library structure makes sense.

Can I run this on a mini PC?

Yes. A modest N100 or similar mini PC is enough for many beginner setups, especially if you are mostly doing direct play instead of heavy transcoding.

Should I choose Plex instead of Jellyfin?

If you want open-source software and more control, Jellyfin is the stronger fit for many homelabbers. If you want an easier commercial ecosystem, Plex still has appeal. Since HomelabAddiction already has a Jellyfin vs Plex vs Emby comparison, that is a good next read if you are still deciding.

What if I only want to manage files I already own?

That is completely valid. Jellyfin still makes sense on its own, and Sonarr or Radarr can remain optional until you need structured automation.

What is the safest way to add remote access later?

Use a reverse proxy with TLS, then protect admin routes with an auth layer such as Authentik. Do not start by opening raw ports to the internet.

What to learn next

Once this stack is running locally, the next skills worth adding are:

The nice thing about this stack is that it grows with you. You do not need to become the kind of person who memorizes every container flag by heart. You just need a clean layout, a repeatable Compose file, and enough patience to understand what each service is responsible for.

That is what makes a homelab sustainable.

Not the biggest stack. Not the flashiest screenshots. Just a system you can explain, maintain, and trust.

Sources and verification

Primary documentation checked: Jellyfin container documentation, the Servarr Docker Guide, and Seerr documentation. Verification focus: documented requirements, service boundaries, configuration assumptions, and recovery-safe operation.

transmission signupstatus: open channel

article_topic // Self-Hosted Media Stack on Docker

Start building a smarter homelab.

Join readers learning Proxmox, networking, storage, backups, and self-hosting without breaking everything.

Enter your email address to receive the Homelab Addiction newsletter.

Beginner-friendly

No gatekeeping. Just clear, actionable guides.

1 useful email / week

Practical tips, real-world setups, and lessons learned.

Zero hype, practical only

What works, what breaks, and how to fix it.

Reply to any email with what you're building.
I read and reply to as many as I can.

— The Homelab Addiction Operator

support // the lab

Found this guide useful?

If it saved you time or a rebuild, you can support more practical homelab guides.

Support the lab