This guide is focused on building a completely autonomous request-based media server using individual docker containers.
Most of the images we will be using are maintiained by linuxserver.io. They maintain many up-to-date versions of the most popular tools used for media servers.
Our full stack includes:
- Plex Media Server - Plex Media Server is the back-end component to Plex, a self-hosted media platform.
- Transmission - A lightweight torrent downloading client.
- Radarr - Web application and tool that integrates directly with most torrent clients for monitoring downloading movies.
- Sonarr - Web application and tool that integrates directly with most torrent clients for monitoring and downloading TV shows.
- Jackett - An indexing tool to be used in conjunction with Sonarr/Radarr.
- Ombi - Web application that automatically gives your shared Plex users the ability to request content.
- Tautulli - A Python based monitoring and tracking tool for Plex Media Server.
- Heimdall - A dashboard to organize and view the status of all your apps.
- LetsEncrypt - For generating SSL certs and reverse proxy to subdomain all our apps on our domain.
- Docker and docker-compose installed on your machine.
- A domain with DNS management.
- A VPN provider supported by OpenVPN.
- A storage device to keep your large files.
These are the instructions on setting up your server and DNS records.
You will need to set up your DNS records to point to your server and enable subdomains.
You will need to create an A record to point to your server.
In the name field, you will put your domain name (alternatively, most DNS providers support using the @ character).
In the IPv4 field, you will put your global IP address. If you don't have a static IP, you will have to use your dynamically assigned one given to you by your ISP. There are a couple methods to find this (make sure you don't have any VPN services running, or your IP address will be different):
- From your network on any internet-accessible device, navigate to ipv4.icanhazip.com
- From the command line, run
curl ipv4.icanhazip.com
These records are important since they allow your domain to have subdomains, i.e. sonarr.mydomain.com. If your DNS provider supports wildcard CNAME records, you will only need to create one. Otherwise, you will have to create a CNAME record for each app you want to be able to access. LetsEncrypt doesn't have a web interface, and you can access your Plex server from app.plex.tv, so you can exclude those.
The first thing we want to do is create a user that will be able to read and write the contents of our docker volumes.
Create the user mediaman with a user ID of your choosing.
Note: ID's from 0-999 are reserved, use any number above that.
sudo useradd -u 2020 --create-home mediaman
Set mediaman's password.
sudo passwd mediaman
And log in to mediaman's bash.
su mediaman
Next, you will want to give mediaman access to the directories we will be using. In my case, I have a hard drive mounted under /mnt/disk1. We don't want to give access to the entire disk as we may end up putting more data on there in the future, so we create subdirectories in it just for this setup. Let's create folders for our media and torrent files.
sudo mkdir /mnt/disk1/media
sudo mkdir /mnt/disk1/torrents
Before we go ahead and give mediaman access to these directories, we need to create the folders our services will need to use since the chown -R
command does not apply to directories created after you run the command.
People typically divide their Plex libraries into a directory each. So, let's create directories for movies and tv. You can create more based on your needs.
sudo mkdir /mnt/disk1/media/movies
sudo mkdir /mnt/disk1/media/tv
Transmission's default structure has downloads and watch (watch is if you want to manually place torrent files in the directory, which Transmission will recognize and begin torrenting).
sudo mkdir /mnt/disk1/torrents/downloads
sudo mkdir /mnt/disk1/torrents/watch
Give mediaman access to directories media and torrents, recursively:
sudo chown -R mediaman:mediaman /mnt/disk1/media
sudo chown -R mediaman:mediaman /mnt/disk1/torrents
My directory structure is set up as such: /home/mediaman/docker-automatic-media-server/[service name]. I have a subfolder inside the docker-automatic-media-server/ folder with each app's name, i.e. /home/mediaman/docker-automatic-media-server/sonarr. In our case, every single one of our apps use a config folder, which we want to be accessible from the same directory for simplicity's sake, so in each one of those subfolders, create another folder called config/.
Now we will be writing our docker-compose files and starting the docker containers. I will leave out most info on how to setup the actual application from the web interfaces to keep this guide as short as possible. Refer to the Docker Hub link under each section.
Note: Remember to change any volume paths to work on your machine!
docker-compose.yml
version: '2'
services:
plex:
image: linuxserver/plex
container_name: plex
network_mode: host
environment:
- PUID=2020
- PGID=2020
- VERSION=docker
volumes:
- /home/mediaman/docker-automatic-media-server/plex/config:/config
- /mnt/disk1/media/movies:/movies
- /mnt/disk1/media/tv:/tv
restart: unless-stopped
if you have a different VPN provider, you can use a different image as opposed to the qmcgaw/private-internet-access image and update the docker-compose file with it. The important thing is that the Transmission service uses the same network as the VPN service, which is done with the following line: network_mode: service:[name of service]. If you update the docker-compose file, remember to expose the Transmission service's ports in the VPN service ports section.
docker-compose.yml
version: '3.7'
services:
pia:
build: https://github.com/qdm12/private-internet-access-docker.git
image: qmcgaw/private-internet-access
container_name: pia
init: true
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
environment:
- USER=[USERNAME]
- PASSWORD=[PASSWORD]
- ENCRYPTION=strong
- PROTOCOL=udp
- REGION=US Chicago
- NONROOT=no
- DOT=on
- BLOCK_MALICIOUS=on
- BLOCK_NSA=off
- UNBLOCK=
- FIREWALL=on
- EXTRA_SUBNETS=
- TINYPROXY=on
- TINYPROXY_LOG=Critical
- TINYPROXY_USER=[TINYPROXY_USERNAME]
- TINYPROXY_PASSWORD=[TINYPROXY_PASSWORD]
- SHADOWSOCKS=on
- SHADOWSOCKS_LOG=on
- SHADOWSOCKS_PASSWORD=[SHADOWSOCKS_PASSWORD]
network_mode: bridge
ports:
# pia
- 8888:8888/tcp
- 8388:8388/tcp
- 8388:8388/udp
# transmission
- 9091:9091
- 51413:51413
- 51413:51413/udp
restart: unless-stopped
transmission:
image: linuxserver/transmission
container_name: transmission
depends_on:
- pia
network_mode: service:pia
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
- USER=[USERNAME]
- PASS=[PASSWORD]
volumes:
- /home/mediaman/docker-automatic-media-server/transmission/config:/config
- /mnt/disk1/torrents/downloads:/downloads
- /mnt/disk1/torrents/watch:/watch
restart: unless-stopped
docker-compose.yml
version: '2'
services:
radarr:
image: linuxserver/radarr
container_name: radarr
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
volumes:
- /home/mediaman/docker-automatic-media-server/radarr/config:/config
- /mnt/disk1/media/movies:/movies
- /mnt/disk1/downloads:/downloads
network_mode: bridge
ports:
- 7878:7878
restart: unless-stopped
docker-compose.yml
version: '2'
services:
sonarr:
image: linuxserver/sonarr
container_name: sonarr
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
volumes:
- /home/mediaman/docker-automatic-media-server/sonarr/config:/config
- /mnt/disk1/media/tv:/tv
- /mnt/disk1/torrents/downloads:/downloads
network_mode: bridge
ports:
- 8989:8989
restart: unless-stopped
docker-compose.yml
version: '2'
services:
jackett:
image: linuxserver/jackett
container_name: jackett
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
volumes:
- /home/mediaman/docker-automatic-media-server/jackett/config:/config
- /mnt/disk1/downloads:/downloads
network_mode: bridge
ports:
- 9117:9117
restart: unless-stopped
docker-compose.yml
version: '2'
services:
ombi:
image: linuxserver/ombi
container_name: ombi
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
- BASE_URL=/
volumes:
- /home/mediaman/docker-automatic-media-server/ombi/config:/config
network_mode: bridge
ports:
- 3579:3579
restart: unless-stopped
docker-compose.yml
version: '2'
services:
tautulli:
image: linuxserver/tautulli
container_name: tautulli
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
volumes:
- /home/mediaman/docker-automatic-media-server/tautulli/config:/config
- /home/mediaman/docker-automatic-media-server/plex/config/Library/Application Support/Plex Media Server/Logs:/logs
network_mode: bridge
ports:
- 8181:8181
restart: unless-stopped
docker-compose.yml
version: '2'
services:
heimdall:
image: linuxserver/heimdall
container_name: heimdall
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
volumes:
- /home/mediaman/docker-automatic-media-server/heimdall/config:/config
network_mode: bridge
ports:
- 81:80
- 444:443
restart: unless-stopped
For my case, I only wanted the subdomains to get an SSL certificate since I will eventually be hosting my own website through my main URL from a different machine
docker-compose.yml
version: '2'
services:
letsencrypt:
image: linuxserver/letsencrypt
container_name: letsencrypt
cap_add:
- NET_ADMIN
environment:
- PUID=2020
- PGID=2020
- TZ=America/Chicago
- URL=[YOUR_DOMAIN]
- SUBDOMAINS=heimdall,jackett,ombi,plex,radarr,raneto,sonarr,tautulli,transmission
- VALIDATION=http
- EMAIL=[YOUR_EMAIL]
- ONLY_SUBDOMAINS=true
volumes:
- /home/mediaman/docker-automatic-media-server/letsencrypt/config:/config
network_mode: bridge
ports:
- 80:80
- 443:443
restart: unless-stopped
If you want the same thing as me (only using subdomains), you can leave the /home/mediaman/docker-automatic-media-server/letsencrypt/config/nginx/site-confs/default file as-is.
To configure your server with subdomains, head to /home/mediaman/docker-automatic-media-server/letsencrypt/config/nginx/proxy-confs/. You will see a bunch of sample files that are already written up for you to use. However, they are pre-configured for docker containers using host networking. Since we are using bridge networking, we will need to update these subdomain files. Here's an example of sonarr.subdomain.conf:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name sonarr.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
location / {
include /config/nginx/proxy.conf;
proxy_pass http://127.0.0.1:8989;
}
location ~ (/sonarr)?/api {
include /config/nginx/proxy.conf;
proxy_pass http://127.0.0.1:8989;
}
}
After you set up all your applications to communicate with eachother, you now have a completely automatic flow. From an Ombi movie request by a shared Plex user, to Sonarr/Radarr, to Jackett for indexing, back to Sonarr/Radarr, to Transmission for downloading, and straight into your library folders. You also have an awesome dashboard with Heimdall, you can set up the API key so that you can see an overview of your apps (Transmission leeching/seeding speed, Sonarr/Radarr queued movies) and direct links to those apps. Tautulli gives you analytics and live data on who's watching what, and also lets you send out weekly newsletters automatically of the recently added movies and TV shows.
Enjoy!