DockerHomelabMedia

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 explore philosophy before going to the technical parts, I’ll leave it at that, and jump into the technical breakdown on the whats and hows. If even that is too much for you, you can jump to the end for the yamls.

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? 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 to access services on your network, how p2p file sharing works. Advanced knowledge is 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. Sonarr/Radarr/Lidarr forward the torrent to QBittorent, which then downloads the media.
  5. Sonarr/Radarr/Lidarr monitor 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
  8. All the while Watchtower runs in the background, periodically checking for updates to the container images.

The tools are pretty amazing: they automatically monitor the feeds you configure, and pull new movies/songs/episodes once they’re released without you needing to check up on them. They even provide a calendar for the release dates, so whenever you’re feeling impatient you can check when something will be available.

You’re okay with a 4k BDRip needing 20GB, but you don’t want a 720p WEBRip to take up the same space? You’re okay with old cartoons being DVD quality, but want all new shows to be at least 1080p? No problem, you can configure detailed qulity profiles, including release type, acceptable file size range, and quality, with ranked preferences.

Plex then automatically pulls a slew of metadata for all your media and keeps even astoundingly large libraries organized. If you want to share it with family, tautulli will help you keep a tight grip on your resources: you can kill resource hogging paused streams, limit transcoding options, get advanced statistics, send a plethora of notifications (including when new media is added!) through scripts, webhooks and chatbots, and a lot more, especially if you integrate things like JBOPS.

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
    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 in my container 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 files 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.

A very important note here: qbittorrent’s default setting binds to any available network device. When you create the VPN, a new virtual device will be created, most likely called tun0. Your VPN connection will be ran through this device. If you leave qBittorrent’s setting as default, it might use your non-vpn connection to download. To avoid leaking your IP like this, make sure to go into Tools > Options > Advanced and set your network interface to tun0. DO NOT SKIP THIS.

Screenshot showing the device settings in qbittorrent

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 platform 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
    environment:
      - PUID=1000
      - PGID=1000
      - VERSION=docker
    volumes:
      - /path/to/your/config/plex:/config
      - /path/to/your/library:/media_library
ports:
- 32400:32400 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>

Once you’ve started your stack, you should be able to access your newly started services:

Name Function Default Port
qBittorrent Torrent client 8080
Jackett Indexer feeds: will be used to search for downloads 9117
Sonarr Library manager for TV shows 8989
Radarr Library manager for movies 7878
Lidarr Library manager for music 8686
Bazarr Library manager for subtitles 6767
Tautulli Monitoring and other extra functions for Plex 8181
Plex Media library manager & streaming platform 32400

Further setup is fairly straightforward:

  1. Access qBittorrent and set a password
  2. I cannot stress this strongly enough:

    SET THE NETWORK INTERFACE TO YOUR VPN TUNNEL!!!


    Details above.
  3. Set up the indexers in Jackett
  4. Use their Torznab Feeds to add them as indexers in Sonarr/Radarr/Lidarr 
  5. Connect Sonarr/Radarr/Lidarr to qBittorrent using the password you’ve set 
  6. Set up your libraries in Sonarr/Radarr/Lidarr and Plex
  7. Set up quality profiles for your media in Sonarr/Radarr/Lidarr
  8. Add your shows, movies, music etc.
  9. 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.

Leave a Reply