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.
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.

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.

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.
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:
- A Linux host with Docker and Docker Compose installed
- At least 8 GB RAM if you want the stack to feel comfortable
- Enough storage for config files and your media library
- A user account with permission to access your media directories
- 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:
- N100 mini PC for a low-power starter host - check current options on Amazon
- Large external SSD for faster library moves and backups - check Samsung T7 options on Amazon
- UPS battery backup so your library database does not get interrupted by a power cut - check entry-level APC UPS options on Amazon
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.
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.
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:
- Create your admin account.
- Add a Movies library pointed at
/media/movies. - Add a TV Shows library pointed at
/media/tv. - 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
- Go to Settings -> Media Management and enable rename options you actually want.
- Add a root folder:
/tv - Under Settings -> General, copy your API key somewhere safe.
In Radarr
- Go to Settings -> Media Management
- Add a root folder:
/movies - 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
- Connect Bazarr to Sonarr and Radarr using their API keys.
- Set your preferred subtitle languages.
- Run a test sync.
Jellyseerr
- Connect Jellyseerr to Jellyfin.
- Connect it to Sonarr and Radarr using their URLs and API keys.
- 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.
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:
- Use strong unique passwords for every admin account.
- Put the stack behind a reverse proxy with TLS.
- 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:
- Stage 1 - Jellyfin + folder layout
- Stage 2 - Add Sonarr and Radarr
- Stage 3 - Add Bazarr and Jellyseerr
- Stage 4 - Add reverse proxy, HTTPS, and SSO
- 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:
- Reverse proxying with Nginx Proxy Manager
- HTTPS and certificates with this HomelabAddiction SSL/TLS guide
- Authentication and MFA with Authentik
- Safer Docker structure with Docker Compose best practices
- Official docs for deeper app-specific behavior:
- Docker Compose documentation
- Jellyfin documentation
- Servarr wiki for Sonarr and Radarr
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.
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.
