Project Overview This project aims to provide a private stack of applications to manage and consume media. It offers an all-in-one solution, covering many needs out of the box. One notable feature is its configuration to use a Cloudflare tunnel pointing to Jellyfin. If you prefer not to use this feature, you can skip the tunnel configuration steps or remove/comment out the cloudflared image from the Docker compose file.
READARR HAS BEEN DEPRCATED, WILL FIND A ALTERNATIVE WHEN I CAN FIND THE TIME
Prerequisites and Notes Before beginning, ensure you have the following:
- Cloudflare Domain: You need a registered domain with Cloudflare.
- NordVPN Subscription: This guide assumes you are using NordVPN. If you use a different VPN provider, please adjust accordingly.
- Debian 12 Install: The guide is tested and configured for Debian 12. Other Linux distributions may work with some adjustments, but results may vary.
- User with Sudo Access: Ensure you have a user on the Debian machine with sudo privileges.
- Directory Structure: This script will create directories in a specific location and assumes this location for all steps. If you need to change this, note that it may disrupt the process. It is recommended to create a link to your target directory or, for an NFS/SMB share, mount inside the directory.
- Command Line Familiarity: Basic knowledge of command-line commands like cd, ls, sudo, nano, and ssh is necessary.
- Workstation: Use a separate workstation from the host running the mediastack. This will make copying tokens and commands easier.
Please note this has been written for a Debian12 OS. All other Linux distros may require some custom modifications.
This also assumes the OS has been installed and configured, with the user having sudo permissions. It would also be a good idea to put a static IP on the console, but it is not strictly required.
-
sudo apt install -y curl nano git openssh-server jq -
With SSH installed, it is easier to use your normal workstation. The rest of this tutorial assumes you have a normal workstation with standard capabilities.
-
Download this git repo for local use, you can place it anywhere, but i recommend the user home directory:
-
sudo git clone https://github.com/alethio36/mediastackThis should have created a folder called
mediastackinside the folder you ran the previous command. Run thelscommand to confirm.
To prepare your NordVPN and Cloudflare tokens, follow the steps below:
- Log in to NordVPN: Visit the NordVPN website and log in to your account.
- Access Token Generation:
- Navigate to the sidebar on the left and click on NORDVPN under Services.
- Scroll down to the Setup VPN Manually section. You will be prompted to verify your email.

- Generate a new access token. You can choose between a 30-day token or an indefinite token. Note that if you choose an expiration token, you will need to redo this step once the token expires.

- Important: This token will not be shown again, so make sure to copy it to your desktop on notepad or something.
- Log in to Cloudflare: Visit the Cloudflare website and log in to your account.
- Navigate to Zero Trust Section:
- Go to the Cloudflare Zero Trust section.
- Under Networks, click on Tunnels.
- Create a New Tunnel:
- Choose the Cloudflared option.
- Name the tunnel as desired.
- Under Install and Run Connectors, copy the Docker command provided.
- Paste this command into a notepad (alongside your NordVPN token) and remove everything that is NOT the token. The token will appear as a series of random characters.
Once you have obtained both tokens, you can proceed with setting up the mediastack.
Now back to the media server, make sure your SSH in and sudo works (a good test is to run the command sudo whoami; it should return "root". If not, please troubleshoot).
- use this command to enter the medisatck folder
cd mediastack
- use this command to run the installer script
sudo bash installer.sh
-
Hit option 1
-
Hit option 2
-
Hit option 4, and enter your NordVPN token
-
Hit option 6, and enter your Cloudflare token
-
Hit option 3. This may take a moment as it downloads all the containers. This option also makes sure to prune and clean up various files and images and updates to the latest image for all the apps.
-
Hit option 7, and start the stack.
-
Optional step: hit option 5 to run a DNS leak test on the running images. This will take a few minutes. Make sure to read the readout of each container. Also check the IP on each container just in case. at the moment, i need to reconfiure how every container works with the leak test. there may be some errors with certain apps. last i checked, the important stuff works.
everything is up and running! You may now go to each app in your browser to configure (more info is below on app configuration) as needed from their respective web GUIs, especially Jellyfin, as we will be opening it to WAN in the next step.
Now, if you return to Cloudflare, it should have the connector loaded up. Hit next. From the next page, please enter your subdomain and domain (subdomain.domain.tld). Below, under service, select http, and under URL, enter "mediaserverip:8096" (making sure to add your media servers actual ip address. Then save the tunnel. Now Jellyfin is open to the World Wide Web. If you would like a second tunnel for Jellyseerr (or whatever), just add a second public hostname in the tunnell configuration.
I can’t find a way to auto-configure all the apps to work with each other. This must be done manually from the GUI of each app. More info below in the . . .
-
Find Your IP Address:
- Find your (hopefully static) IP address of the host server.
- Enter
YOURIP:83/adminin your browser.
-
Configuration Video:
- Watch this video for a detailed walkthrough.
- This tutoiral also is very brief on how to use PI-hole, there is a lot it can do and if you want the most out of it, check out their documentation or relevant guides on youtube
-
DNS Record:
- Use your host IP address (where everything is running).
- Use your basic domain (e.g.,
YOURDOMAIN.TLD).
-
CNAMEs:
- Suggestion:
service.YOURDOMAIN.TLD(e.g.,jellyfin.YOURDOMAIN.TLD). - Remember the service name for future steps.
- Suggestion:
-
DNS Configuration on Client Machines:
- Point your machine's DNS lookups to the host IP address.
- Some routers allow network-level DNS configuration.
- Consult your IT professional or documentation for more information.
- For local machine settings, search for "My OS DNS settings."
-
Upstream DNS in Pi-hole:
- Under Pi-hole settings and DNS, configure upstream DNS lookups.
- Use a more private DNS if preferred.
-
Using Pi-hole for DHCP:
- Beyond this manual's scope, but can be configured.
- Might be a good option for static IP addresses.
-
Access NPM:
- Enter
MEDIASTACKIP:81in your browser (Eg. 192.168.1.41:81). - Default credentials:
admin@example.comandchangeme.
- Enter
-
Add Proxy Host:
-
Advanced Configuration for Pi-hole:
- In the Advanced tab, enter the following:
location / { proxy_pass http://IPADDRESS:83/admin/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_hide_header X-Frame-Options; proxy_set_header X-Frame-Options "SAMEORIGIN"; proxy_read_timeout 90; } location /admin { proxy_pass http://IPADDRESS:83/admin/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_hide_header X-Frame-Options; proxy_set_header X-Frame-Options "SAMEORIGIN"; proxy_read_timeout 90; }
- Change
IPADDRESSto your (hopefully) static IP. - Just in case, here is the orginal instruction to add pihole to the npm.
- In the Advanced tab, enter the following:
-
Add Other Services:
- Repeat the above steps (excluding the Advanced configuration for Pihole) for other services.
- Example services: qbittorrent, Sonarr, Radarr, Prowlarr, Lidarr, Readarr, Bazarr, Jellyfin, Jellyseerr, NPM, Pihole.
-
Testing Proxy Hosts:
- Test each proxy host in your browser.
- Use incognito mode or a different browser to avoid cache issues.
-
SSL Setup (using Cloudflare):
- Log into Cloudflare, go to API Tokens, and create a token using the "Edit Zone DNS" template. Please also rename the token, in case you need to change it later.
-
-
-
- Save the token, cloudflare will NOT show it again.
- In NPM, go to SSL Certificates, add a certificate, and select Let's Encrypt.
- Domain name:
*.YOURDOMAIN.TLD. - Email address: Your real email for Let's Encrypt.
- Use DNS challenge with Cloudflare.
-
- Add
120to propagation settings, agree to TOS, and save. It may take a minute or two to confirm, thats normal. - Edit each host to use the new SSL certificate.
-
-
Initial Configuration:
- Create an account to log in.
- Adjust root folder settings:
/data/media/*subfolder*(e.g., TV for Sonarr, Movies for Radarr). - Note the API key under settings, General.
-
Remote Mapping:
- Configure remote mapping under Download Client.
- It should be something like this:
Host Remote Path Local Path localhost /torrent/ /data/torrent/
-
Settings Adjustments:
- adjusting settings for the specifcs of each app can take a significant amount of time and depend heavily on preference. I recommend waiting until everything is configured before doing this, but it is up to you.
- This guide is widley recommended if you are not sure where to start. (TRaSH Guides)[https://trash-guides.info/]
-
Add a download client
- Go to settings, Download clients, and add qbittorrent, it should look something like this:
-
Integrate prowlarr
- In Prowlarr, go to settings, apps, and add each service. For sync level I recommend Full Sync
- Welcome Wizard:
- Configure Jellyfin with the welcome wizard.
- Add media folders (e.g., Movies and TV).
- Delay library scan until after basic setup.
- Make sure there is at least a single admin account (usualy your own)
- Make note of the jellyfin API token.
- Welcome Wizard:
- Use the connect to Jellyfin button.
- Connect Radarr and Sonarr using their IP address or domain name and API key.
- Access and Configure:
- Enter the correct domain into the browser.
- Default credentials:
admin/adminadmin. - If login fails, check the logs for the real password with the folowing command:
sudo docker logs qbittorrent - Configure settings, user login, and anonymous mode.
- Optionally, disable CSRF protection if encountering unauthorized errors for some reason, if i would hit a bookmark or something to come this this adress, it comes up with a unauthorized error. When i clicked the url and hit enter, worked like a charm. To fix this (optional) go to tools, options, web ui tab, then about halfway down, theres a box checked for Enable Cross-Site Request Forgery (CSRF) protection. Unchecking that fixed the issue. buts its not critical so id recommend keeping it unless you have a reason to need this to work
-
Hopefully in the future, i will have the installer.sh script handle this, but for now
-
Edit Docker Compose:
- Open the Docker compose file:
sudo nano docker-compose.yml - Adjust the following lines under the gluetun configuration. They do not all need to be entered, I would work top down, and configure as desired:
- SERVER_COUNTRIES= # Comma separated list of countries - SERVER_REGIONS= # Comma separated list of regions - SERVER_CITIES= # Comma separated list of server cities - SERVER_HOSTNAMES= # Comma separated list of server hostnames - SERVER_CATEGORIES= # Comma separated list of server categories
- Open the Docker compose file:
- Final Steps and notes:
-
Ensure all services are connected and configured.
-
Make sure to connect Prowlarr to all ARR apps.
-
Leave the ARR stack qBittorrent connection to localhost.
-
Start scanning all libraries in jellyfin, sonarr, radarr, jellyseerr. This may take a substantial amount of time. As in several days depending on your pcs power. please save this step for the very last.
-
Perhaps run a final dns leak check from
installer.shjust to make sure -
If you are running on a NAS, the config files will become unstable and unhappy, especially the SQLite database files. I would suggest a directory (such as /ms5) thats held locally and adjust your docker compose file accordingly. It will also be WAY snappier.
-
Iv been told Qbittorrent cant be a smudge fussy about what port its on. If you go to Tools->Options->Connections and hit the random button (and save) you may have a better time of things
-
There may be some time out related errors with the ARR apps, please add the below code into the advanced section in the NPM manager host section for effected services.
# Increase timeouts send_timeout 100m; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 30m;
-
-
- Install docker (done)
-
- Install NordVPN and wireguard (done)
-
- Pull the wireguard key and place it into compose file (done)
-
- Update docker images
-
- Prune docker images
-
- Configure storage options for containers (this will need to interact with the compose file) and create said directories as well (won’t know until needed for specifics). Unfortunately, this may be beyond the scope of this project.
-
- Update host system (done)
-
- Perform an IP and DNS leak test on all containers (done)
-
- Transfer a Windows Jellyfin to a Linux Jellyfin server (this was not done well and should be considered beyond the scope of this project)
-
- Put in proper error handling in user interaction
-
- Add an option to change VPN tunnel options
-
- Only install wanted apps
-
- Auto-configure all the ARR apps with each other (this is not going to happen, too complicated)
-
- Setup DNS, proxy, and SSL with the apps for LAN access (this mediastack has the options for all these things but must be manually configured)
-
- Setup Jellyfin and Jellyseerr to work with Cloudflare tunnel
-
- Setup a docker secrets for sensitive tokens
- xxxx:xxxx # gluetun -vpn - VPN tunnel
- 8085:8085 # qbittorrent -vpn - Torrenter
- 8989:8989 # Sonarr -vpn - TV
- 7878:7878 # Radarr -vpn - Movie
- 9696:9696 # Prowlarr -vpn - Torrent Indexer
- 8686:8686 # Lidarr -vpn - Music
- 8787:8787 # Readarr -vpn - Books
- 6767:6767 # Bazarr -vpn - Subtitles
- 8096:8096 # Jellyfin -no vpn - Media Player
- 5055:5055 # Jellyseerr - no vpn - Media Grabber
- 8191:8191 # Flaresolver - vpn - Cloudflare Captcha Solver
- 80:80 443:443 81:81 (81 is the admin portal) # NPM (proxy) - no vpn - This routes your domains to services
- 53:53 67:67 83:80 (83 is the external mgmt portal) # Pihole - no vpn - This is a DNS, DHCP, and adblocker
- xxxx:xxxx # Watchtower for automatic image updater
- curl
- nano
- git
- wireguard
- docker
- nordvpn
- ssh
- docker
sh <(curl -sSf https://downloads.nordcdn.com/apps/linux/install.sh)
It will ask for the sudo password.
Setup an access token to log in. Set to not expire (or do, but it will expire in 30 days).
https://gist.github.com/bluewalk/7b3db071c488c82c604baf76a42eaad3
There’s no legacy, log in via nordvpn login --token *place token here*.
Note, logging out invalidates the token unless you pass an argument, check on that before logging out of NordVPN via terminal:
nordvpn logout --persist-token
Keep your current access token valid after logging out. (default: false)

