Deep Dive into a Self-Hosted Media Management Platform for Beginners

What exactly are we building?

A fancy docker compose stack consisting of:

  • A built in VPN container, through which all other containers’ traffic is routed
  • Plex: A media library management tool that organizes and streams your media files.”
  • Tautulli: Companion software to Plex, fine-grained control for monitoring and managing your streams
  • Sonarr, Radarr, Lidarr, Bazarr: Automating the downloads of TV shows, movies, music, and subtitles (in that order)
  • Jackett: API-based management of indexers for the aforementioned list of download clients
  • QBittorrent: Torrent client
  • Watchtower: Tool to auto-update the containers


The Reasons

If you’re here, you’ve probably heard the famous words from Valve’s founder:

        Piracy is an issue of service, not price.

– Gabe Newell

Since I myself can’t stand technical articles that ramble on and on pointlessly, I’ll leave it at that, and jump into the technical breakdown on the whats and hows.

I obviously don’t advocate doing anything illegal. This tutorial is entirely theoretical, and no law abiding citizen should have something like this running in their closet, supplying them with and endless stream of entertainment media. Absolutely not.

Piracy is bad, mmkay?

Piracy is bad, mmkay?

Piracy is bad, mmkay?

 

 

 

 

 


Prerequisites

A small breakdown of the skills, tools, & technologies that are needed to use this:

Hardware

  • Server: I strongly suggest setting this up on a dedicated server. The hardware requirements are relatively low (I used to run this on an old laptop before upgrading), but once you get into heavy transcoding you might want to upgrade.
  • Storage: While not strictly necessary, I can with absolute certainty say that you will end up needing lots and lots of storage. I’m currently up to 40TB, and it’s starting to run out.

Software

  • Operating system: Not a strict requirement, but I strongly suggest using Linux. Docker on windows uses a self-managed Linux VM anyways. + Everything I mention runs in a Linux environment for me, and I haven’t tested the setup on Windows, so I won’t be able to definitively answer questions or troubleshoot on Windows.
  • Docker: docker must be installed and configured to be used with docker compose. Obviously, since this is a tutorial for setting this up with docker compose.
  • Docker Compose: see above.

Know-how

  • CLI: I strongly advise being at least somewhat comfortable using a CLI – You won’t be working with it too much in the scope of this tutorial, but you shouldn’t be scared of it.
  • Docker: You should know at least the basics of what containers are, how they work, how they communicate with each other, and most importantly, how volumes work.
  • Networking: you’ll need a basic understanding of what a VPN is, how port forwarding works, how p2p file sharing works. Advanced knowledge if recommended if you want to do deeper configs – VPN chaining, putting the services behind a reverse proxy etc.

The Setup

An outline of the setup

  1. Sonarr/Radarr/Lidarr monitor the release of media, bazarr downloads subtitles for newly downloaded series and movies.
  2. Once something is released, it polls the indexers/trackers configured in Jackett.
  3. Jackett gives back a list of torrents to the programs, which pick the one most fitting for your configured profile (quality+filesize+release type).
  4. S/R/L forwards the torrent to QBittorent, which then downloads the media.
  5. S/R/L monitors the download, and once it’s done, imports it into the specified media folders.
  6. Plex monitors the media folders, and automatically picks up the newly downloaded media. It sorts it into your libraries, downloads metadata for it, and provides your own streaming platform for it. Think of it as your private netflix.
  7. Anything that happens on Plex, Tautulli keeps track of it; it tracks and manages your streams, it can send you emails/chat notifications of newly added media and software updates; if you integrate things like JBOPS it can do even more.
  8. All the while Watchtower runs in the background, periodically checking for updates to the container images.

Details

First thing’s first: you should keep your media and configs organized. It’ll save you a lot of headaches later on. I suggest a  directory structure similar to this:

mediastack
├── config
│   ├── bazarr
│   ├── jackett
│   ├── lidarr
│   ├── plex
│   ├── qbittorrent
│   ├── radarr
│   ├── sonarr
│   ├── tautulli
│   └── vpn
└── library
    ├── downloads
    ├── movies
    ├── music
    └── tvshows

The directories under media will be shared between the containers, and each container will have a dedicated config directory. This makes troubleshooting as well as backing your data up significantly easier – I’ve migrated my install between multiple servers by now, and all it took each time was to 1:1 shift the config directories between the machines and restart the stack.

Next up are the containers.

In my opinion, the most curcial part of the setup is the VPN; if you use the right one, you can hide your activity behind it (at least well enough that you won’t be bugged by your ISP.)

I use NordVPN, but I’m in no ways trying to endorse it or advocate for it. I strongly suggest you look into what works best for you.

After trying out many, many different VPN images that worked with varying success, I’ve decided to build my own. It’s simple, it’s provider-agnostic, and you can read more details on github.

The start of your docker-compose.yml should look like this:

version: '3'
services:
  vpn:
    container_name: 'vpn'
    image: valtimalti/openvpn-client:latest
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun
    network_mode: bridge
    ports:
      - 8080:8080
      - 9117:9117
      - 6767:6767
      - 8686:8686
      - 7878:7878
      - 8989:8989
    environment:
      - LOCAL_NETWORK=192.168.0.0/24
    volumes:
      - '/path/to/your/config:/config'

A few things to note here:

  • These are the default ports for the applications that you will use. If you want to change the port on the host, you can simply change the port on the left side of the mappings. If you want to change the application ports, you’ll need to look up how to do it in the documentation of the docker images, and change the right side as well.
  • The LOCAL_NETWORK variable should be set to your local network CIDR, you can get this from your router. If you want to do things like VPN chaining you can also use your VPN’s subnet. Support for more networks is incoming, but is not a priority as of now.
  • The config directory on your host machine should have at least one (or more) .ovpn file in it for your VPN connection.

The rest of the apps will all look like this:

qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:14.3.9
    restart: 'unless-stopped'
    container_name: 'qbittorrent'
    depends_on:
      - vpn
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Vienna
      - WEBUI_PORT=8080
    volumes:
      - /path/to/your/config/qbittorrent/config:/config
      - /path/to/your/library/downloads:/downloads
    network_mode: service:vpn

  jackett:
    image: 'linuxserver/jackett'
    restart: 'unless-stopped'
    container_name: 'jackett'
    depends_on:
      - vpn
    volumes: 
      - /path/to/your/config/jackett/config:/config
      - /path/to/your/library/downloads:/downloads
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Vienna
    network_mode: service:vpn
  
  bazarr:
    image: 'linuxserver/bazarr'
    restart: 'unless-stopped'
    container_name: 'bazarr'
    depends_on:
      - vpn
      - jackett
      - qbittorrent
    volumes:
      - /path/to/your/config/bazarr:/config
      - /path/to/your/library/movies:/movies
      - /path/to/your/library/tvshows:/tvshows
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Vienna
    network_mode: service:vpn

  lidarr:
    image: 'linuxserver/lidarr'
    restart: 'unless-stopped'
    container_name: 'lidarr'
    depends_on:
      - vpn
      - jackett
      - qbittorrent
    volumes:
      - /path/to/your/config/lidarr/config:/config
      - /path/to/your/library/downloads:/downloads
      - /path/to/your/library/music:/music
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Vienna
    network_mode: service:vpn
    
  radarr:
    image: 'linuxserver/radarr'
    restart: 'unless-stopped'
    container_name: 'radarr'
    depends_on:
      - vpn
      - jackett
      - qbittorrent
    volumes:
      - /path/to/your/config/radarr/config:/config
      - /path/to/your/library/movies:/data/movies
      - /path/to/your/library/downloads:/downloads
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Vienna
    network_mode: service:vpn

  sonarr:
    image: 'linuxserver/sonarr'
    restart: 'unless-stopped'
    container_name: 'sonarr'
    depends_on:
      - vpn
      - jackett
      - qbittorrent
    volumes:
      - /path/to/your/config/sonarr/config:/config
      - /path/to/your/library/tvshows:/data/tvshows
      - /path/to/your/library/downloads:/downloads
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Vienna
    network_mode: service:vpn

There are two very important features used here: the depends_on and network_mode keys.

The network_mode: service: vpn set here will tunnel your traffic through the VPN container, and is also the reason why the ports are mapped on the VPN container, and not here. This can cause some conflicts (e.g. if a port is used by more than one container) and there are other ways to do it, but for the purpose of this tutorial this is a perfectly funcitonal solution.

The depends_on key first and foremost  makes sure that the VPN container is already running before the other containers are started. I’ve also jackett and qbittorrent for the release managers, since they poll the services upon container start, and will complain if they’re unreachable at the time.

 

For plex and tautulli, I use a separate stack. Tautulli is harmless, and my plex installation is ran through the official UI of plex itself, is reachable on app.plex.tv, and is only shared with my family, so I felt that putting it behind a VPN would only be a drag on network speed for relatively low added security. You can, however, still choose to put it behind your VPN by adding them to the stack above, mapping their ports, and setting their network_mode.

Here’s the YAML:

version: "3"
services:
  tautulli:
    image: ghcr.io/linuxserver/tautulli:latest
    container_name: tautulli
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/London
    volumes:
      - /path/to/your/config/tautulli:/config
    ports:
      - 8181:8181
    restart: unless-stopped
  plex:
    image: ghcr.io/linuxserver/plex:latest
    container_name: plex
    network_mode: host
    environment:
      - PUID=1000
      - PGID=1000
      - VERSION=docker
    volumes:
      - /path/to/your/config/plex:/config
      - /path/to/your/library:/media_library
    restart: unless-stopped

Optionally, you can use watchtower to manage updating your containers:

watchtower:
  image: 'containrrr/watchtower'
  restart: 'unless-stopped'
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    - /etc/localtime:/etc/localtime:ro
  command: --cleanup --schedule "0 0 * * * *" --rolling-restart <list of the containers you want to manage>

 

Further setup is fairly straightforward:

  1. Set up the indexers in Jackett (Port: 9117)
  2. Use their Torznab Feeds to add them as indexers in S/R/L (Ports 8989, 7878, 6767 respecitvely)
  3. Connect S/R/L to QBittorrent (Port 8080)
  4. Set up your libraries in S/R/L and Plex
  5. Add your shows, movies, music etc.
  6. Start watching!

That’s about it. You should be ready to start streaming your favourite movies, TV shows, and music.

The full YAMLs can be found here for the media management and here for plex.

Share: