A calmer way to self-host

Build the server you actually want.

Modular Media Server is a friendly guide and Compose generator for putting together your own little corner of the internet. Start with one thing. Add another when you need it.

No giant mystery box

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.

01

Pick your pieces

Choose media, photos, monitoring, downloads, archives and more.

02

We'll sort the plumbing

Required dependencies are added automatically and explained.

03

Tell us where things live

Set your storage roots once instead of wrestling with volume mappings.

04

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.

The toolbox

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

Management

A friendly Docker management platform for viewing, managing and working with Compose projects.

Jellyfin

Media

A self-hosted media server for organizing and streaming your movies, shows, music and other media.

Jellyseerr

Requests

A request and discovery interface that connects your media server with services such as Sonarr and Radarr.

Sonarr

TV

Automates the management of TV libraries by monitoring for releases, importing files and keeping names organized.

Radarr

Movies

Automates movie collection management, including monitoring, importing, renaming and quality upgrades.

Lidarr

Music

A music collection manager that monitors artists and helps organize and upgrade your library.

Gluetun

Networking

A lightweight VPN client container supporting multiple providers, OpenVPN and WireGuard.

qBittorrent

Downloads

A full-featured BitTorrent client with a web interface, commonly paired with a VPN container for isolated download traffic.

Jackett

Indexing

A proxy and translation layer that provides a standardized API between applications and torrent trackers.

Bazarr

Subtitles

A companion to Sonarr and Radarr that manages subtitle downloads according to your language preferences.

Immich

Photos

A high-performance self-hosted photo and video management system for backing up, viewing, organizing and sharing your library.

Kima

Music

A 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

Monitoring

An easy-to-use self-hosted monitoring tool for services, hosts, containers, certificates and more.

ArchiveBox

Archives

A self-hosted web archiving tool that preserves pages and other web content in durable, local formats.

Tube Archivist

Archives

A self-hosted YouTube media server for downloading, organizing and watching your personal video archive.

The useful bit

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.

18modules across media, downloads, photos, monitoring, connection and authentication
6files written — Compose, .env, .env.example, .gitignore, Caddyfile, README
0data sent anywhere; everything runs locally in your browser
docker-compose.ymlgenerated for you
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"
Start here

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.

You need: a machine that stays on — a mini PC, an old laptop, a NAS or a VPS — with Docker installed. Docker setup for Windows, macOS, Linux or NAS →
About the command line. Step one is the only place you have to type commands, and they are all copy-and-paste. Once Arcane is running, everything after it — including every service you add later — is done by clicking in a browser. If that is still more than you want, a Synology or QNAP NAS can create the Arcane container from its own app store with no terminal at all.
7 steps · about 30 minutes
  1. 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:3552 and 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.
  2. 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.yml now 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/stacks works; /opt/stacks:/app/data/projects quietly breaks relative paths.
  3. 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-stopped

    Change /srv/media to wherever your films and shows live, and set your timezone. Press Deploy. The first run downloads a few hundred megabytes.

    :ro means read-only. Jellyfin has no reason to modify your files, so it is not allowed to.
  4. 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 run ls /media. Nothing listed means the volume path in step 3 does not match your real folder.

    Naming decides whether artwork matches. The Thing (1982).mkv works; film.final.copy.mkv rarely does.

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

    Deploy it, then open http://your-server-ip:3001 and 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.
  6. 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 URL http://jellyfin:8096 — using the container name, because inside Docker localhost means Kuma itself. Between the two you learn both that it exists and that it works.
  7. 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.

A small but important thing

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.

Torrents are also a security concern.
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.