Launching a full stack Plex setup including nginx reverse proxy, as well as automatic cert generation by LetsEncrypt.

Requirements

  • Environment that has Docker available.
  • Disk space so you can bring together your library.
  • A domain name that will tie everything together!

What we’ll be setting up

  • Sonarr - so you can monitor and download your favorite series
  • Radarr - same as above, but for movies
  • Transmission + OpenVPN - so you can fetch your media
  • Jackett - to keep up to date lists of where you can fetch from
  • Plex - so you can stream your movies
  • Portainer - so you can easily observe the state of the docker containers on your host
  • Nginx-Proxy with LetsEncrypt - to reverse proxy and always have up to date certs!

Lets get going!

Helper containers

First off, we’ll boot our nginx-proxy container:

docker run --detach \
    --name nginx-proxy \
    --publish 80:80 \
    --publish 443:443 \
    --restart unless-stopped \
    --volume certs:/etc/nginx/certs \
    --volume vhost:/etc/nginx/vhost.d \
    --volume html:/usr/share/nginx/html \
    --volume /var/run/docker.sock:/tmp/docker.sock:ro \
    nginxproxy/nginx-proxy

After that, we boot our nginx-proxy-acme which is going to link us up to the LetsEncrypt services so we can get certificates and keep them valid. You will need to supply an email address in the env var below:

docker run --detach \
    --name nginx-proxy-acme \
    --restart unless-stopped \
    --volumes-from nginx-proxy \
    --volume /var/run/docker.sock:/var/run/docker.sock:ro \
    --volume acme:/etc/acme.sh \
    --env "DEFAULT_EMAIL=<your.email@email.com>" \
    nginxproxy/acme-companion

Once we have the above 2 containers running, we can setup Portainer. This will be the first container which we will have a public URL for:

docker run -d --name portainer \
  --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
	-e VIRTUAL_HOST=portainer.mydomain.com \
	-e LETSENCRYPT_HOST=portainer.mydomain.com \
	-e VIRTUAL_PORT=9000 \
	portainer/portainer-ce:latest

Once the above has been started, navigate to portainer.mydomain.com and setup authentication for your Portainer. This interface will allow you to monitor the status of your containers without having to SSH into the remote box every time, making it a much more convenient job.


OK - now that the helper containers have been setup we can jump into the functional ones!

For this setup to work smoothly and to avoid having duplicated files on your drive, it’s recommended to mount only a single folder into the docker containers /data and then nest the new folders inside of that. The folder structure that this will produce, should look something like this:

data
|-- tv-series
|-- movies
|-- downloads

This ensures that files can be linked using hard links.

Another important tidbit is to ensure that the owners of the folder are set to user ID 1000, group 1000, as we will insert PGID and PUID into all our docker containers with values of 1000. This can be achieved with a simple command: chown -R 1000:1000 data/.

Functional containers

Transmission downloader

The below will boot up the transmission-openvpn container, and as you can see the paths for the downloads folder are /data/downloads/completed. Since this path will be the same in all containers Radarr/Sonarr will know from where to pickup finished downloads.

docker run --cap-add=NET_ADMIN -d \
  --name=transmission-openvpn \
  -v /root/data/:/data \
  -v /etc/localtime:/etc/localtime:ro \
  -e CREATE_TUN_DEVICE=true \
  -e OPENVPN_PROVIDER=<provider> \
  -e OPENVPN_CONFIG=<config> \
  -e OPENVPN_USERNAME=<username> \
  -e OPENVPN_PASSWORD=<password> \
  -e WEBPROXY_ENABLED=false \
  -e TRANSMISSION_UMASK=0 \
  -e TRANSMISSION_RATIO_LIMIT=2 \
  -e TRANSMISSION_RATIO_LIMIT_ENABLED=true \
  -e TRANSMISSION_DOWNLOAD_DIR=/data/downloads/completed \
  -e TRANSMISSION_HOME=/data/downloads/transmission-home \
  -e TRANSMISSION_INCOMPLETE_DIR=/data/downloads/incomplete \
  -e TRANSMISSION_DOWNLOAD_QUEUE_ENABLED=false \
  -e PGID=1000 \
  -e PUID=1000 \
  --net local-discovery \
  --log-driver json-file \
  --log-opt max-size=10m \
  --dns 8.8.8.8 \
  --dns 8.8.4.4 \
  --restart unless-stopped \
  haugene/transmission-openvpn

Jackett

docker run \
  --name=jackett \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Europe/London \
  -e VIRTUAL_HOST=jackett.mydomain.com \
  -e LETSENCRYPT_HOST=jackett.mydomain.com \
  -e VIRTUAL_PORT=9117 \
  -v /root/cfg/jackett/:/config \
  -v /root/data:/data \
  --restart unless-stopped \
  linuxserver/jackett

For Jackett, we also have a public URL, it’s important to navigate there and make sure you setup authentication, otherwise anyone will be able to access this.

Sonarr

docker run \
  --name=sonarr \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Europe/London \
  -v /root/cfg/sonarr:/config \
  -v /root/data:/data \
  -e VIRTUAL_HOST=sonarr.mydomain.com \
  -e LETSENCRYPT_HOST=sonarr.mydomain.com \
  -e VIRTUAL_PORT=8989 \
  --restart unless-stopped \
  linuxserver/sonarr

Same as the above, once this is booted up, you want to navigate to the URL and setup authentication. This can be done from Settings -> General-> Security.

Radarr

docker run \
  --name=radarr \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Europe/London \
  -v /root/cfg/radarr:/config \
  -v /root/data:/data \
  -e VIRTUAL_HOST=radarr.mydomain.com \
  -e LETSENCRYPT_HOST=radarr.mydomain.com \
  -e VIRTUAL_PORT=7878 \
  --restart unless-stopped \
  linuxserver/radarr

Same as the above, once this is booted up, you want to navigate to the URL and setup authentication. This can be done from Settings -> General-> Security.

Plex

docker run \
  --name=plex  \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Europe/London \
  -e VERSION=docker \
  -e VIRTUAL_HOST=plex.mydomain.com \
  -e LETSENCRYPT_HOST=plex.mydomain.com \
  -e VIRTUAL_PORT=32400 \
  -v /root/cfg/plex:/config \
  -v /root/data:/data \
  -v /root/data/transcode:/transcode \
  --device=/dev/dri:/dev/dri \
  --restart unless-stopped \
  linuxserver/plex

We are also mountain /dev/tri which will allow Plex to use hardware acceleration when this would make sense for transcoding media files. However, /dev/tri is not always available by default and in some cases one might need to manually install new drivers, or enable them. This is not a requirement to get Plex running, however, it does mean you will get more performance out of your Plex setup.

Conclusions

With all of the above containers running you should have a fully setup and ready to go Plex environment. Happy streaming!

There are no exposed ports on any of the docker containers, with the exception of the nginx one, reducing the risk of us allowing rogue traffic onto our server. All of the available URLs should also be behind authentication, so this is a nice added bonus.