🛠️ Why I Built This

This project started with frustration: Plex, Sonarr, Radarr, and all the other media tools were installed individually on a Windows desktop environment, each with its own startup quirks, update annoyances, and performance issues. Over time, I found the system growing unstable and increasingly annoying to manage.

I decided to rebuild the entire thing from the ground up using Docker. It gave me portability, isolation, performance — and most of all, maintainability. What used to be a headache is now a self-healing stack I can scale, tweak, or back up easily.

My goal with this article is to document the entire system for future reference, help friends replicate the build, and guide Plex users to tools like Overseerr to request new content easily.

🔩 The Hardware

This server is a beast from another era, but it still runs like a champ with a few upgrades:

  • Intel Core i7-7700K
  • 32GB DDR4 RAM
  • GTX 1080 GPU (for Immich's CUDA acceleration)
  • Windows 10 Pro running Docker Desktop with Portainer

Storage is a blend of old and new: an 8TB WD external holds the current Plex library, while a RAID 5 array of 4x16TB SAS drives (via an expansion card + Rosewill front bay) supports Immich and acts as backup storage for Plex content if the WD ever fails.

🔗 How It All Works Together

Each stack is deployed via Portainer and lives in its own YAML file. Services communicate using shared volumes and bridged networks, with traffic securely routed by Nginx.

Flow:
  Overseerr ➝ Sonarr/Radarr ➝ NZBGet ➝ Plex Library
                        ↳ Requests auto-download via indexers

  Prowlarr ➝ Controls all indexers
  Jackett ➝ Adds compatibility for niche/private trackers
  Gluetun ➝ Wraps Qbittorrent traffic in VPN
  Immich ➝ Full photo backup + GPU AI tagging
  Nginx ➝ Subdomain routing w/ SSL (e.g. immich.[domain].duckdns.org)

Each piece fits like a cog in a wheel — media gets requested via Overseerr, found by *arr apps, downloaded via NZBGet or Qbit (through VPN), and ends up in Plex for everyone to enjoy.

📜 Sample Stack Configurations

Plex Stack (Redacted)
services:
  nzbget:
    image: linuxserver/nzbget:latest
    container_name: nzbget
    ports:
      - "6789:6789"
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/New_York
      - NZBGET_USER=<REDACTED>
      - NZBGET_PASS=<REDACTED>
    volumes:
      - nzbget-config:/config
      - C:/Users/Eldradia/Downloads:/downloads
    restart: unless-stopped

  sonarr:
    image: linuxserver/sonarr:latest
    container_name: sonarr
    ports:
      - "8989:8989"
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/New_York
    volumes:
      - sonarr-config:/config
      - C:/Users/Eldradia/Downloads:/downloads
      - E:/:/media
    restart: unless-stopped

  ... (radarr, readarr, prowlarr, jackett, overseerr, flaresolverr follow similar structure)
Immich Stack
version: '3.9'
services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:release
    ports:
      - "2283:2283"
    environment:
      - TZ=America/New_York
      - DB_HOST=database
      - DB_PORT=5432
      - DB_USERNAME=immich_user
      - DB_PASSWORD=<REDACTED>
      - DB_DATABASE_NAME=immich
    volumes:
      - P:/immich:/usr/src/app/upload
      - P:/immich/data:/usr/src/app/data
    depends_on:
      - redis
      - database
    restart: unless-stopped
    runtime: nvidia
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:release-cuda
    environment:
      - TZ=America/New_York
      - TRANSFORMERS_CACHE=/cache
      - ONNXRuntimeExecutionProvider=CUDAExecutionProvider
    volumes:
      - P:/immich/data:/cache
    runtime: nvidia
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]
    restart: unless-stopped

  redis:
    container_name: immich_redis
    image: redis:6.2-alpine
    volumes:
      - P:/immich/data/redis:/data
    restart: unless-stopped

  database:
    container_name: immich_postgres
    image: tensorchord/pgvecto-rs:pg14-v0.2.0
    environment:
      - POSTGRES_USER=immich_user
      - POSTGRES_PASSWORD=<REDACTED>
      - POSTGRES_DB=immich
    volumes:
      - P:/immich/data/postgres:/var/lib/postgresql/data
    restart: unless-stopped
Valheim Stack
services:
  valheim:
    image: lloesche/valheim-server
    container_name: valheim
    cap_add:
      - sys_nice
    ports:
      - "2456-2457:2456-2457/udp"
    volumes:
      - C:/Valheim Server:/config
      - C:/Valheim Server/configs:/home/steam/.config/unity3d/IronGate/Valheim
    environment:
      - SERVER_NAME=Eldradia Reborn
      - WORLD_NAME=Eldradia
      - SERVER_PASS=<REDACTED>
      - SERVER_PUBLIC=false
      - BEPINEX=true
      - TZ=America/New_York
    restart: unless-stopped

Self-hosting was the best decision I’ve made for tech freedom and control. If you want to try it yourself — reach out. I’ll share everything I’ve learned.

Built with Docker, powered by curiosity, and made for the long haul.