Take it one piece at a time.
Pick the services you want and the generator handles the boring connections between them, while still showing you what is happening.
Pick your pieces
Choose media, photos, monitoring, downloads, archives and more.
We'll sort the plumbing
Required dependencies are added automatically and explained.
Tell us where things live
Set your storage roots once instead of wrestling with volume mappings.
Take the files home
Get Compose, environment files, secrets and a README ready to review.
New to this? There is a step-by-step walkthrough further down that gets you from an empty machine to a working server.
Meet the apps.
These are the projects Modular Media Server is designed to help you understand and combine. Each one has its own project, documentation and license; the links below point back to the original sources.
Arcane
ManagementA friendly Docker management platform for viewing, managing and working with Compose projects.
Jellyfin
MediaA self-hosted media server for organizing and streaming your movies, shows, music and other media.
Jellyseerr
RequestsA request and discovery interface that connects your media server with services such as Sonarr and Radarr.
Sonarr
TVAutomates the management of TV libraries by monitoring for releases, importing files and keeping names organized.
Radarr
MoviesAutomates movie collection management, including monitoring, importing, renaming and quality upgrades.
Lidarr
MusicA music collection manager that monitors artists and helps organize and upgrade your library.
Gluetun
NetworkingA lightweight VPN client container supporting multiple providers, OpenVPN and WireGuard.
qBittorrent
DownloadsA full-featured BitTorrent client with a web interface, commonly paired with a VPN container for isolated download traffic.
Jackett
IndexingA proxy and translation layer that provides a standardized API between applications and torrent trackers.
Bazarr
SubtitlesA companion to Sonarr and Radarr that manages subtitle downloads according to your language preferences.
Immich
PhotosA high-performance self-hosted photo and video management system for backing up, viewing, organizing and sharing your library.
Kima
MusicA self-hosted audio streaming platform built around your own music library, with playlist import, podcasts, a Subsonic API for native apps, and integrations including Lidarr and Audiobookshelf.
Uptime Kuma
MonitoringAn easy-to-use self-hosted monitoring tool for services, hosts, containers, certificates and more.
ArchiveBox
ArchivesA self-hosted web archiving tool that preserves pages and other web content in durable, local formats.
Let's build something.
Pick the services you want. Dependencies get resolved for you, secrets are generated in your browser, and you leave with a complete set of files ready to deploy.
services:
jellyfin:
image: jellyfin/jellyfin:latest
ports:
- "8096:8096"
volumes:
- ${CONFIG_ROOT}/jellyfin/config:/config
- ${DATA_ROOT}/media:/media:ro
# qBittorrent is tied to Gluetun's network.
# If the VPN drops, it loses its connection too.
qbittorrent:
network_mode: "service:gluetun"
Your first server, in about half an hour.
Three services, end to end: Arcane to manage everything, Jellyfin to watch things, and Uptime Kuma to tell you on Discord when something breaks. Nothing here assumes you have done this before.
Install Arcane
Arcane is the web interface you will use for everything after this. It needs two secrets of its own — run this twice and keep both lines:
openssl rand -base64 32
Make a folder for your stacks, and one inside it for Arcane:
sudo mkdir -p /opt/stacks/arcane cd /opt/stacks/arcane nano docker-compose.yml
Paste this in, replacing both secrets:
services: arcane: image: ghcr.io/getarcaneapp/arcane:latest container_name: arcane ports: - "3552:3552" volumes: - /var/run/docker.sock:/var/run/docker.sock - arcane-data:/app/data # same path on both sides — this matters - /opt/stacks:/opt/stacks environment: - APP_URL=http://localhost:3552 - PUID=1000 - PGID=1000 - ENCRYPTION_KEY=your-first-secret - JWT_SECRET=your-second-secret restart: unless-stopped volumes: arcane-data:docker compose up -d
Open
http://your-server-ip:3552and create your admin account. That is the last command you have to type.Arcane can control your whole machine through the Docker socket. Keep it on your home network — never forward port 3552 to the internet.Point Arcane at your stacks folder
In Arcane, open Settings and set the projects directory to
/opt/stacks— the same path you mounted above.Any folder in there containing a
docker-compose.ymlnow appears under Projects. Arcane itself will already be listed, because you put it there.Matching paths inside and outside the container is the detail people get wrong./opt/stacks:/opt/stacksworks;/opt/stacks:/app/data/projectsquietly breaks relative paths.Create a project for Jellyfin
In Arcane, go to Projects → New Project, call it
jellyfin, and paste this as the Compose file:services: jellyfin: image: jellyfin/jellyfin:latest container_name: jellyfin user: "1000:1000" ports: - "8096:8096" volumes: - /opt/stacks/jellyfin/config:/config - /opt/stacks/jellyfin/cache:/cache - /srv/media:/media:ro environment: - TZ=Europe/London restart: unless-stoppedChange
/srv/mediato wherever your films and shows live, and set your timezone. Press Deploy. The first run downloads a few hundred megabytes.:romeans read-only. Jellyfin has no reason to modify your files, so it is not allowed to.Set up a Jellyfin library
Open
http://your-server-ip:8096. Create your user account, then when it asks for a media library:- Content type: Movies — make a second library later for Shows, since separate types matter for metadata
- Display name: Films
- Folder:
/media/movies— the path inside the container, not the host path
Finish the wizard, then run Scan All Libraries from the dashboard.
If the library comes up empty, the mount is wrong rather than Jellyfin. Check what it can actually see: in Arcane open the Jellyfin container, use the console, and runls /media. Nothing listed means the volume path in step 3 does not match your real folder.Naming decides whether artwork matches.
The Thing (1982).mkvworks;film.final.copy.mkvrarely does.Add Uptime Kuma
Another Arcane project, this one called
uptime-kuma:services: uptime-kuma: image: louislam/uptime-kuma:1 container_name: uptime-kuma ports: - "3001:3001" volumes: - /opt/stacks/uptime-kuma/data:/app/data # lets Kuma watch containers, not just web pages - /var/run/docker.sock:/var/run/docker.sock:ro environment: - TZ=Europe/London restart: unless-stoppedDeploy it, then open
http://your-server-ip:3001and create an account.The socket mount is read-only and only needed for container monitors. If you would rather not grant it, leave that line out and use the HTTP monitor in the next step — it answers the more useful question anyway.Watch the Jellyfin container
First tell Kuma about Docker. Go to Profile → Settings → Docker Hosts → Add Docker Host:
- Friendly name: Local
- Connection type: Socket
- Path:
/var/run/docker.sock
Save, then Add New Monitor:
- Monitor type: Docker Container
- Friendly name: Jellyfin container
- Container name:
jellyfin - Docker host: the one you just created
Add an HTTP monitor as well. A container can be running while the app inside it is wedged. Choose type HTTP(s) and URLhttp://jellyfin:8096— using the container name, because inside Dockerlocalhostmeans Kuma itself. Between the two you learn both that it exists and that it works.Get told on Discord
In Discord, open the channel you want alerts in, then Edit Channel → Integrations → Webhooks → New Webhook. Name it, then Copy Webhook URL.
Back in Uptime Kuma, edit your monitor and choose Setup Notification:
- Notification type: Discord
- Friendly name: Discord
- Discord webhook URL: paste it
- Tick Default enabled so new monitors use it automatically
Press Test — a message should land in the channel within a second or two. If it does not, the webhook URL is wrong; nothing else in the chain produces that result.
A webhook URL is a password. Anyone who has it can post to your channel, so keep it out of screenshots and out of git.
That is a working server.
Jellyfin is serving your library, Arcane manages it without a terminal, and you find out on Discord if it stops. Everything after this is optional.
Self-hosting comes with responsibility.
This project helps you run software. It does not give you permission to download, archive, copy or distribute media you do not have the right to use.
Files can come from untrusted sources and may contain malware, ransomware, trojans, cryptominers or malicious scripts. A VPN can protect network privacy; it cannot make a malicious file safe.
Own your server.
Know what's running on it.