Skip to main content

Pirated Tech

An Imbeciles Guide to Servarr

Table of Contents

# Introduction

About two years ago, a friend found half a dozen ThinkStation P330 Tinys for sale by a local CAD company that was upgrading their machines. $130 and a fresh install of Ubuntu Server later I was the proud owner of my first home server and, as I would soon come to find out, constant source of stress.

My first attempt at a media stack included Jellyfin, Radarr, Transmission, and Jackett. It was outdated, unreliable, and slow—and it lacked a VPN. Frustrated with downloads taking two weeks for a 2GB movie, I eventually gave up. After moving apartments, my little ThinkStation ended up sitting in a closet for nearly a year and a half. That is, until I recently decided to take a break from doomscrolling social media. Boredom turned into productivity, and so I gave it a second shot – this time with a more up-to-date stack and a bit more patience.

After a couple weeks of reading docs, reddit posts, blogs, and forums I am finally satisified with my Servarr setup and thought I would share my experience in case anyone runs into similar roadblocks while setting up their own media stack.

You can find my entire docker-compose.yml and any other configs I used in the appendix at the end of this article.

# Media Stack

Not knowing exactly where to begin, I did some snooping and eventually found a popular guide by navilg. This easy to follow guide got the ball rolling with a dockerized media stack consisting of:

  • Jellyfin,
  • Jellyseerr,
  • Radarr,
  • Sonarr,
  • Prowlarr,
  • qBittorrent,
  • and VPN config.

The guide also has instructions for how to configure Ngnix if you wish to host Jellyfin externally, but I chose to just have a local setup.

# Getting Started

Following the guide by navilg, I got my Docker network set up, cloned the docker-compose.yml, filled out the necessary environment variables and followed the inline comments to configure the settings for the VPN profile. Most guides recommend against using the latest version of the Docker images to avoid incompatibility, but I have never ran into and issue with this while using this stack, so I changed all the image versions to :latest.

For my VPN I chose Proton VPN’s free tier which does the job well as long as you don’t care about port forwarding (we will cover this later). Just make sure you have uncommented FREE_ONLY=on in the docker-compose.yml and set your country to either Netherlands, United States, or Japan.

After finishing the initial setup, I now had all my services configured and running locally. We can now get into some of the aforementioned roadblocks.

# Keep it Simple, Stupid!

My first mistake was wanting to access as many indexers as I could to increase my chances of getting my grubby little hands on as many free movies as possible. This approach required a Flaresolverr proxy configuration as many indexers are protected by Cloudflare DDoS Protection. So, I added Flaresolverr to my docker-compose.yml and tried to traffic it through my VPN.

services:
  flaresolverr:
    profiles: ["vpn", "no-vpn"]
    image: ghcr.io/flaresolverr/flaresolverr:latest
    container_name: flaresolverr
    environment:
      - LOG_LEVEL=info
      - LOG_HTML=false
      - CAPTCHA_SOLVER=none
      - TZ=UTC
    ports:
      - "8191:8191"
    restart: unless-stopped
    network_mode: service:vpn

I connected it to Prowlarr, and tried to add an indexer that required it. This started a multi-day hair pulling exercise in learning about DNS settings and Docker networking as I fought for my life to try to solve the error Unable to connect to indexer, please check your DNS settings and ensure IPv6 is working or disabled. After questioning friends with more Docker and networking experience than myself to no avail, I finally joined the Servarr Discord where I was informed that Flaresolverr has been in shambles since Cloudflare developers have been keeping an eye on it.

Flaresolverr is currently BROKEN. The Cloudflare devs are watching the repo and so it is very likely permanently dead.

Also, we are not the Flaresolverr support team and cannot help you with questions that don’t relate specifically to adding Flaresolverr to Prowlarr.

Flaresolverr is a 3rd party program which solves cloudflare captchas for some indexers. They use Github for support, and you should go there to ask them questions or catch up on the current status of the program.

The current open issue on their github is: FlareSolverr/FlareSolverr#1253"

So after all my efforts, I decided to scrap all hopes of using Flaresolverr tagged indexers. This turned out to be perfectly fine, as sometimes less is more, and I have found having a few highly rated indexers just as good as having them all.

My next hiccup was having Prowlarr passed to my VPN. Although this is the default setting in navilg’s guide, I do not believe it to be correct. All of my indexers would fail almost immediately after restarting the container, so I started digging into logs. After inspecting both Docker and *arr logs there was a lot of HTTP request timed out and HTTP 429: TooManyRequests errors.

I knew this was likely due to my VPN configuration, but no matter what I did (e.g. changing DNS to 1.1.1.1 or 8.8.8.8 in the VPN, containers, dropping the firewall all together, and becoming all too familiar with Gluetun documentation) I just couldn’t get it.

I finally just decided I had to get rid of the VPN and risk getting some threatening mail from my ISP when a friend mentioned that you dont need to have Prowlarr networked through your VPN. It turns out he was right! Although interacting with indexers is a bit shady, its not really illegal. As long as you have your download client handled by the VPN you should be fine. So, I got rid of network_mode: service:vpn for mynetwork, assigned it a static ipv4 address, et voilà, I had functioning indexers!

  prowlarr:
    profiles: ["vpn", "no-vpn"]
    container_name: prowlarr
    image: linuxserver/prowlarr:latest

    networks:
      mynetwork:
        ipv4_address: 172.20.0.40

# Improvements

You should now have a functioning media stack, but if your experience is anything like mine, your torrents are downloading slower than molasses in winter.

I found a reddit post by u/WankWankNudgeNudge and after following their suggestions and giving qBittorrent 4GB of ram instead of the default 512MB, my downloading speeds went from about 1% per day to a finishing whole 30GB file in under an hour.

  • Tools>Options>Connection:

    • Enabled Protocol - set to TCP (just TCP, not TCP and uTP)

    • Uncheck all boxes under Listening Port and Connections Limits.

  • Tools>Options>Speed:

    • Uncheck all boxes under Rate Limits Settings.

You can improve your speeds more by port forwarding either via your router (if you aren’t using the VPN profile) or through your VPN. Port forwarding is not required but if you do not do it, you are restricting yourself to only connecting to peers who do.

I recently upgraded from Proton VPN free tier to Proton VPN Plus to do this, but have not got around to it yet. Apparently my motivation comes from frustration, and my downloads are fast enough for me at the moment. However, if you choose to go this route Proton has a guide to enable port forwarding in your qBittorrent client.

This may not belong under improvements, but for myself, I dislike the default mounting points that the docker-compose.yml provided by navilg has. I opted to migrate these to bind mounts just for ease of access. If you set your mounts from the get-go, just set your path to be whatever you like (e.g. ./volumes). However if you are migrating like I did you will need to change the host mounting point in your docker-compose.yml to something like /home/user/jellyfin/volumes/media-stack_{volumename}/_data. Note the _data directory. Since you are migrating with, I assume, something like cp -r /var/lib/docker/volumes/media-stack_* /home/user/jellyfin/volumes, all of your files will be nested behind _data. This is just the default location used by Docker.

You will also need to delete the volumes from the bottom of your docker-compose.yml.

volumes:
 torrent-downloads:
 radarr-config:
 sonarr-config:
 prowlarr-config:
 jellyfin-config:
 qbittorrent-config:
 jellyseerr-config:

## Using Ubuntu?

If you are using Ubuntu you may be facing a lack of storage space. I only had 98gb free which, at first, I thought was some limit on the torrent-downloads mount or a restriction in my root partition.

I ran lsblk to find out Ubuntu only allocates 100GB to your root filesystem.

nvme0n1                   259:0    0 476.9G  0 disk 
├─nvme0n1p1               259:1    0     1G  0 part /boot/efi
├─nvme0n1p2               259:2    0     2G  0 part /boot
└─nvme0n1p3               259:3    0 473.9G  0 part 
  └─ubuntu--vg-ubuntu--lv 252:0    0   100G  0 lvm  /

I know, I need a bigger hard drive. Anyways, putting all my trust in ChatGPT I simply ran the following commands and gave / 100% of nvme0n1p3:

sudo lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv
sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv

# Afterthoughts

I am now mostly satisified with my media stack, however I still need to make a few tweaks such as port forwarding my VPN, and maybe one day implementing Ngnix so I can access Jellyfin outside of my home network.

Overall, if you avoid overcomplicating and learn from my mistakes you should be watching your favourite shows and movies in no time.

Disclaimer: This article is for informational and educational purposes only. I do not condone or encourage copyright infringement, piracy, or any illegal activities. Users are solely responsible for ensuring that their actions comply with all applicable laws in their jurisdiction. I assume no liability for any misuse of the information provided. Proceed at your own risk.

Have fun sailing the seven seas!

# Appendix

name: media-stack
services:
  vpn:
    profiles: ["vpn"]
    container_name: vpn
    image: qmcgaw/gluetun:latest
    cap_add:
      - NET_ADMIN

    environment:
      - VPN_SERVICE_PROVIDER=protonvpn # Valid values: nordvpn, expressvpn, protonvpn, surfshark or custom
      - OPENVPN_USER=""
      - OPENVPN_PASSWORD=""
      - SERVER_COUNTRIES=Canada
      #- FREE_ONLY=on

    networks:
      - mynetwork

    devices:
      - /dev/net/tun:/dev/net/tun

    ports:
      - 5080:5080
      - 6881:6881
      - 6881:6881/udp

    restart: "unless-stopped"

  qbittorrent:
    profiles: ["vpn", "no-vpn"]
    container_name: qbittorrent
    image: lscr.io/linuxserver/qbittorrent:latest
    network_mode: service:vpn

    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
      - WEBUI_PORT=5080

    volumes:
      - /home/user/jellyfin/volumes/media-stack_qbittorrent-config/_data:/config
      - /home/user/jellyfin/volumes/media-stack_torrent-downloads/_data:/downloads

    restart: "unless-stopped"

  radarr:
    profiles: ["vpn", "no-vpn"]
    container_name: radarr
    image: lscr.io/linuxserver/radarr:latest
    networks:
      mynetwork:
        ipv4_address: 172.20.0.20 # It should be available IPv4 address in range of docker network `mynetwork` e.g. 172.20.0.2  

    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

    ports:
      - 7878:7878

    volumes:
      - /home/user/jellyfin/volumes/media-stack_radarr-config/_data:/config
      - /home/user/jellyfin/volumes/media-stack_torrent-downloads/_data:/downloads

    restart: "unless-stopped"

  sonarr:
    profiles: ["vpn", "no-vpn"]
    image: linuxserver/sonarr:latest
    container_name: sonarr
    networks:
      mynetwork:
        ipv4_address: 172.20.0.30 # It should be available IPv4 address in range of docker network `mynetwork` e.g. 172.20.0.2

    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

    volumes:
      - /home/user/jellyfin/volumes/media-stack_sonarr-config/_data:/config
      - /home/user/jellyfin/volumes/media-stack_torrent-downloads/_data:/downloads

    ports:
      - 8989:8989

    restart: unless-stopped

  prowlarr:
    profiles: ["vpn", "no-vpn"]
    container_name: prowlarr
    image: linuxserver/prowlarr:latest
    networks:
      mynetwork:
        ipv4_address: 172.20.0.40

    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

    volumes:
      - /home/user/jellyfin/volumes/media-stack_prowlarr-config/_data:/config

    ports:
      - 9696:9696

    restart: unless-stopped

  jellyseerr:
    profiles: ["vpn", "no-vpn"]
    image: fallenbagel/jellyseerr:latest
    container_name: jellyseerr
    networks:
      - mynetwork
    dns:
      - 8.8.8.8
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC
      - FORCE_IPV4_FIRST=true

    volumes:
      - /home/user/jellyfin/volumes/media-stack_jellyseerr-config/_data:/app/config

    ports:
      - 5055:5055

    restart: unless-stopped

  jellyfin:
    profiles: ["vpn", "no-vpn"]
    image: linuxserver/jellyfin:10.10.3
    container_name: jellyfin
    networks:
      - mynetwork
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

    volumes:
      - /home/user/jellyfin/volumes/media-stack_jellyfin-config/_data:/config
      - /home/user/jellyfin/volumes/media-stack_torrent-downloads/_data:/data

    ports:
      - 8096:8096
      - 7359:7359/udp
      - 8920:8920

    restart: unless-stopped

networks:
  mynetwork:
    external: true

You can optionally add Readarr and/or Calibre if you enjoy books!

  readarr:
    profiles: ["vpn", "no-vpn"]
    image: lscr.io/linuxserver/readarr:develop
    container_name: readarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

    networks:
      mynetwork:
        ipv4_address: 172.20.0.60 # It should be available IPv4 address in range of docker network `mynetwork` e.g. 172.20.0.2

    volumes:
      - /home/keenan/jellyfin/volumes/media-stack_readarr-config/_data:/config
      - /home/keenan/jellyfin/volumes/media-stack_torrent-downloads/_data:/downloads #optional
    ports:
      - 8787:8787
    restart: unless-stopped

 calibre:
   profiles: ["vpn", "no-vpn"]
   image: lscr.io/linuxserver/calibre:latest
   container_name: calibre
   environment:
     - PUID=1000
     - PGID=1000
      - TZ=Etc/UTC

    networks:
     - mynetwork
    volumes:
     - /home/keenan/jellyfin/volumes/media-stack_calibre-config/_data:/config
     - /home/keenan/jellyfin/volumes/media-stack_torrent-downloads/_data:/data
    ports:
      - 8080:8080
      - 8181:8181
      - 8081:8081
   restart: unless-stopped

Just throw that at the bottom of your docker-compose.yml, but be careful, Calibre’s default webUI port is 8080 which is commonly used, so just make sure you don’t have anything else running on it.