I regularly help people install, configure, and run automated Plex stacks. I have found that unfortunately I spend a lot of time solving the infrastructure stuff before we can even get to the application configs. The various mainstream containers aren’t setup for hot linking, Spaceinvader One has a video that has a dated screenshot in it (not a complaint! he’s awesome!), etc. All things that beginners stumble over. In an attempt to make that first part easy, I created the following.
Assumptions:
-
Torrents and Usenet downloads are wrapped with a VPN, and I use PIA. If you use something else, you’ll want to tweak things as described here. Regardless, even if you use PIA, you need to get the opvn files and add them to the correct directory. The link provided goes into detail. Note that only torrents require port forwarding.
-
You are using a filesystem structure that allows for hot linking. I’ve described a good way to do that here, in the event you need to move your stuff around.
-
This setup is built to run on Windows because that’s where I find most beginners are doing their builds. Adopting it to Mac/Linux is not complicated.
-
Docker’s root directory will be C:\Users<win_username>\Docker. This too is easy to change using the .env file
-
I have not added SWAG/LetsEncrypt because most beginners don’t own domains. I’ve primed the setup though using a custom Docker network that makes adding SWAG easy.
Bonus: put the text below into a decent text editor and collapse the services section
docker-compose.yaml
version: '3.9'
networks:
docker_internal_network:
name: ${DOCKER_NETWORK_NAME}
services:
plex: ######### [http://localhost:32400/web/index.html] PLEX - media server
image: plexinc/pms-docker:plexpass
container_name: ${DOCKER_CONTAINER_PREFIX}_plex
networks:
- docker_internal_network
environment:
- PLEX_CLAIM=${PLEX_CLAIM}
- ADVERTISE_IP=${PLEX_ADVERTISE_IP}
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${PLEX_TRANSCODE_PATH}:/transcode
- ${DOCKER_APPDATA_PATH}\plex:/config
- ${DATA_PATH}:/data
ports:
- '32400:32400'
- '3005:3005'
- '8324:8324'
- '32469:32469'
- '1900:1900/udp'
- '32410:32410/udp'
- '32412:32412/udp'
- '32413:32413/udp'
- '32414:32414/udp'
restart: unless-stopped
ombi: ######### [http://localhost:3579] OMBI - user media requests and trouble tickets
image: linuxserver/ombi:development
container_name: ${DOCKER_CONTAINER_PREFIX}_ombi
networks:
- docker_internal_network
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\ombi:/config
ports:
- '3579:3579'
restart: unless-stopped
radarr: ####### [http://localhost:7878] Radarr - movie management
image: linuxserver/radarr
container_name: ${DOCKER_CONTAINER_PREFIX}_radarr
networks:
- docker_internal_network
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\radarr:/config
- ${DATA_PATH}:/data
ports:
- '7878:7878'
restart: unless-stopped
sonarr: ####### [http://localhost:8989] Sonarr - TV management
image: linuxserver/sonarr
container_name: ${DOCKER_CONTAINER_PREFIX}_sonarr
networks:
- docker_internal_network
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\sonarr:/config
- ${DATA_PATH}:/data
ports:
- '8989:8989'
restart: unless-stopped
lidarr: ####### [http://localhost:8686] Lidarr - music management
image: linuxserver/lidarr
container_name: ${DOCKER_CONTAINER_PREFIX}_lidarr
networks:
- docker_internal_network
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\lidarr:/config
- ${DATA_PATH}:/data
ports:
- '8686:8686'
restart: unless-stopped
bazarr: ####### [http://localhost:6767] Bazarr - subtitle managment
image: ghcr.io/linuxserver/bazarr
container_name: ${DOCKER_CONTAINER_PREFIX}_bazarr
networks:
- docker_internal_network
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\bazarr:/config
- ${DATA_PATH}:/data
ports:
- '6767:6767'
restart: unless-stopped
tautulli: ##### [http://localhost:8181] Tautulli - stats
image: linuxserver/tautulli
container_name: ${DOCKER_CONTAINER_PREFIX}_tautulli
networks:
- docker_internal_network
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\tautulli:/config
- ${DOCKER_APPDATA_PATH}\plex\Library\Application Support\Plex Media Server\Logs:/logs
ports:
- '8181:8181'
restart: unless-stopped
nzbhydra2: #### [http://localhost:5076] NZBHydra2 - meta search for newznab indexers and torznab trackers
image: linuxserver/nzbhydra2
container_name: ${DOCKER_CONTAINER_PREFIX}_nzbhydra2
networks:
- docker_internal_network
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\nzbhydra2:/config
- ${DATA_PATH}:/data
ports:
- '5076:5076'
restart: unless-stopped
jackett: ###### [http://localhost:9117] Jackett - indexer scraping & translation logic
image: ghcr.io/linuxserver/jackett
container_name: ${DOCKER_CONTAINER_PREFIX}_jackett
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\jackett:/config
- ${DATA_PATH}:/data
ports:
- '9117:9117'
restart: unless-stopped
deluge-vpn: ### [http://localhost:8112] Delude VPN + Privoxy - torrent downloader, wrapped in a VPN, also running Privoxy [8118]
image: binhex/arch-delugevpn
container_name: ${DOCKER_CONTAINER_PREFIX}_deluge-vpn
cap_add:
- NET_ADMIN
environment:
- VPN_ENABLED=yes
- VPN_USER=${VPN_USERNAME}
- VPN_PASS=${VPN_PASSWORD}
- VPN_PROV=pia
- VPN_CLIENT=openvpn # connection files located at https://www.privateinternetaccess.com/openvpn/openvpn.zip
- STRICT_PORT_FORWARD=yes
- ENABLE_PRIVOXY=no
- LAN_NETWORK=${VPN_LAN_SUBNET}
- NAME_SERVERS=209.222.18.222,84.200.69.80,37.235.1.174,1.1.1.1,209.222.18.218,37.235.1.177,84.200.70.40,1.0.0.1
- DELUGE_DAEMON_LOG_LEVEL=error
- DELUGE_WEB_LOG_LEVEL=error
#- VPN_INPUT_PORTS=1234
#- VPN_OUTPUT_PORTS=5678
- DEBUG=false
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- '/etc/localtime:/etc/localtime:ro'
- ${DOCKER_APPDATA_PATH}\deluge-vpn:/config
- ${DATA_PATH}:/data
ports:
- '8112:8112'
- '8118:8118'
- '58846:58846'
- '58946:58946'
restart: unless-stopped
sabnzbd-vpn: ## [http://localhost:8080] SABnzbd VPN - usenet downloader, wrapped in a VPN
image: binhex/arch-sabnzbdvpn
container_name: ${DOCKER_CONTAINER_PREFIX}_sabnzbd-vpn
cap_add:
- NET_ADMIN
environment:
- VPN_ENABLED=yes
- VPN_USER=${VPN_USERNAME}
- VPN_PASS=${VPN_PASSWORD}
- VPN_PROV=pia
- VPN_CLIENT=openvpn # connection files located at https://www.privateinternetaccess.com/openvpn/openvpn.zip
- STRICT_PORT_FORWARD=no
- ENABLE_PRIVOXY=no
- LAN_NETWORK=${VPN_LAN_SUBNET}
- NAME_SERVERS=209.222.18.222,84.200.69.80,37.235.1.174,1.1.1.1,209.222.18.218,37.235.1.177,84.200.70.40,1.0.0.1
#- VPN_INPUT_PORTS=1234
#- VPN_OUTPUT_PORTS=5678
- DEBUG=false
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- /etc/localtime:/etc/localtime:ro
- ${DOCKER_APPDATA_PATH}\sabnzbd-vpn:/config
- ${DATA_PATH}:/data
ports:
- '8080:8080'
- '8090:8090'
#- '8118:8118' # disabled because privoxy is running in the torrent container
restart: unless-stopped
gaps: ######### [http://localhost:8484] GAPS - identifies movie collections and missing films
container_name: ${DOCKER_CONTAINER_PREFIX}_gaps
environment:
- PUID=1000
- PGID=1000
- TZ=${DOCKER_TIME_ZONE}
volumes:
- ${DOCKER_APPDATA_PATH}\gaps:/config
ports:
- '8484:8484'
restart: unless-stopped
dozzle: ####### [http://localhost:9999] Dozzle - Docker log reader
image: amir20/dozzle:latest
container_name: ${DOCKER_CONTAINER_PREFIX}_dozzle
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- '9999:8080'
restart: unless-stopped
.env
# Docker DOCKER_NETWORK_NAME = swag DOCKER_CONTAINER_PREFIX = media-server-stack DOCKER_TIME_ZONE = America/Los_Angeles DOCKER_APPDATA_PATH = C:\Users\Jesse\Docker\appdata # Top-Level Location of Your Media and Incoming Data DATA_PATH = Z:\ # Plex PLEX_CLAIM = PLEX_ADVERTISE_IP = http://localhost:32400/ PLEX_TRANSCODE_PATH = C:\Users\Jesse\Docker\transcode # VPN VPN_USERNAME = p####### VPN_PASSWORD = ???????????????????? VPN_LAN_SUBNET = 10.0.0.0/24
For those new to Docker Compose, using these two files looks like this:
C:\Users\Jesse\Docker>docker ps --format "table {{.Names}}\t| {{.State}}\t| {{.Status}}"
NAMES | STATE | STATUS
C:\Users\Jesse\Docker>docker-compose up -d
Creating network "swag" with the default driver
Creating network "docker_default" with the default driver
Creating media-server-stack_plex ... done
Creating media-server-stack_dozzle ... done
Creating media-server-stack_sabnzbd-vpn ... done
Creating media-server-stack_lidarr ... done
Creating media-server-stack_deluge-vpn ... done
Creating media-server-stack_radarr ... done
Creating media-server-stack_ombi ... done
Creating media-server-stack_gaps ... done
Creating media-server-stack_jackett ... done
Creating media-server-stack_sonarr ... done
Creating media-server-stack_tautulli ... done
Creating media-server-stack_nzbhydra2 ... done
Creating media-server-stack_bazarr ... done
C:\Users\Jesse\Docker>docker ps --format "table {{.Names}}\t| {{.State}}\t| {{.Status}}"
NAMES | STATE | STATUS
media-server-stack_tautulli | running | Up 7 minutes
media-server-stack_sonarr | running | Up 7 minutes
media-server-stack_bazarr | running | Up 7 minutes
media-server-stack_radarr | running | Up 7 minutes
media-server-stack_nzbhydra2 | running | Up 7 minutes
media-server-stack_deluge-vpn | running | Up 7 minutes
media-server-stack_jackett | running | Up 7 minutes
media-server-stack_gaps | running | Up 7 minutes
media-server-stack_lidarr | running | Up 7 minutes
media-server-stack_sabnzbd-vpn | running | Up 7 minutes
media-server-stack_ombi | running | Up 7 minutes
media-server-stack_dozzle | running | Up 7 minutes
media-server-stack_plex | running | Up 7 minutes (healthy)
C:\Users\Jesse\Docker>
This should have all of your apps running and ready for configuration.