Skip to main content

2. Docker Compose File

Sweet! So you want to install the arr stack on your self hosted device. Well, as with any app you need decide which app workflow deployment you want to use. Here, we focus on the docker compose deployment using a dedicated VM. Now, personally I like to run the arr stack in a dedicated VM and Jellyfin in another Docker VM. This gives you the possibility to move your Jellyfin instance around easily to different hosts and if something is wrong with your arr stack then your users can still enjoy the already downloaded media. Eventually, it is up to you and the amount of resources you have at your disposal what your decision will be. Hence, you can also run all of this in your existing docker vm. 

1. Create a new docker vm by using the proxmox helper scripts ;) Or if you want to continue using your existing docker vm then you need 

2. 

I keep every tunable value in a .env file rather than hardcoding it in the Compose file. Ports, paths, and the user IDs all live in one place, which makes the Compose file readable and easy to reuse. I also run every service on an external network called media_network, which means the network is created once, outside of Compose, and the stack attaches to it. That keeps the network stable even when I tear the stack down and bring it back up.

Create a project directory and the .env file:

Hottio Decluttar

Whisparr is the Sonarr/Radarr equivalent for adult content 

Whisper-AI generates subtitles on the fly for your media

 

compose.yml
services:
# Sonarr - TV Shows
  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./sonarr/config:/config
      - ${ROOT_MEDIA_PATH}:/data # Access to /data/downloads and /data/media/tv
    ports:
      - 8989:8989
    restart: unless-stopped
    networks:
      - internal-proxy

  # Radarr - Movies
  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./radarr/config:/config
      - ${ROOT_MEDIA_PATH}:/data # Access to /data/downloads and /data/media/movies
    ports:
      - 7878:7878
    restart: unless-stopped
    networks:
      - internal-proxy

networks:
  internal-proxy:
    driver: bridge

# Prowlarr - Indexer centralized management
  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./prowlarr/config:/config
    ports:
      - 9696:9696
    restart: unless-stopped
    networks:
      - internal-proxy

# Profilarr & Decluttarr (Config only, no media access needed usually)
  profilarr:
    image: santiagosayshey/profilarr:latest
    container_name: profilarr
    environment:
      - TZ=${TZ}
    volumes:
      - ./profilarr/config:/config
    ports:
      - 6868:6868
    restart: unless-stopped
    networks:
      - internal-proxy
    depends_on:
      - sonarr
      - radarr

# FlareSolverr - Bypasses Cloudflare protection for Indexers
  flaresolverr:
    image: ghcr.io/flaresolverr/flaresolverr:latest
    container_name: flaresolverr
    environment:
      - LOG_LEVEL=${LOG_LEVEL:-info}
      - TZ=${TZ}
    ports:
      - 8191:8191
    restart: unless-stopped
    networks:
      - internal-proxy
	  
# Gluetun - VPN Client
  #  gluetun:
#
    image: qmcgaw/gluetun:latest
    #    container_name: gluetun
    #    cap_add:
#
      - NET_ADMIN
    #    environment:
#
      - PUID=${PUID}
      #      - PGID=${PGID}
#
      - TZ=${TZ}
      #      - VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
#
      - VPN_TYPE=${VPN_TYPE}
      #      - WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
#
      - WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
      #      - VPN_PORT_FORWARDING=${VPN_PORT_FORWARDING}
    #    ports:
#
      - 8888:8888 # Health check and control
      #      - 6881:6881 # qBittorrent P2P port
      #      - 6881:6881/udp
    #    restart: unless-stopped
    #    networks:
#
      - internal-proxy

# If using Gluetun, use qbittorrent from linuxserver io, NOT the hotio image below!
  #qbittorrent:
#qbittorrent:
    image: lscr.io/linuxserver/qbittorrent
#
    network_mode: "service:gluetun" # The "Magic" line
    #    depends_on:
      #      gluetun:
#
        condition: service_healthy # Wait for VPN to be up first

#qbittorrent-vpn:
#    image: hotio/qbittorrent:latest
#    container_name: qbittorrent-vpn
#    cap_add:
#      - NET_ADMIN
#    environment:
#      - PUID=${PUID}
#      - PGID=${PGID}
#      - TZ=${TZ}
#      - VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
#      - VPN_USER=${VPN_USER}
#      - VPN_PASS=${VPN_PASS}
#      - OPENVPN_CONFIG=${VPN_OPENVPN_CONFIG}
#    volumes:
#      - ./qbittorrent-vpn/config:/config
#      - ${ROOT_MEDIA_PATH}:/data # This maps the whole root to /data
#    ports:
#      - 8080:8080
#      - 6881:6881
#      - 6881:6881/udp
#    restart: unless-stopped
#    networks:
#      - internal-proxy
	  
# Lidarr - Music
  lidarr:
    image: lscr.io/linuxserver/lidarr:latest
    container_name: lidarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./lidarr/config:/config
      - ${ROOT_MEDIA_PATH}:/data
    ports:
      - 8686:8686
    restart: unless-stopped
    networks:
      - internal-proxy

  # Bazarr - Subtitles
  bazarr:
    image: lscr.io/linuxserver/bazarr:latest
    container_name: bazarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./bazarr/config:/config
      - ${ROOT_MEDIA_PATH}:/data
    ports:
      - 6767:6767
    restart: unless-stopped
    networks:
      - internal-proxy

  # whisper-ai - Local Subtitle Generation (Requires GPU)
  whisper-ai:
    image: ${WHISPER_IMAGE_URL} # Use a pre-built image like "jellyfin-whisper-lab/whisper-container"
    container_name: whisper-ai
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./whisper/config:/config
      - ${ROOT_MEDIA_PATH}:/data
    # Enable GPU access if you have one, or comment out the 'deploy' section for CPU-only:
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: nvidia
    #           count: all
    #           capabilities: [gpu]
    restart: unless-stopped
    networks:
      - internal-proxy

  # Decluttarr - Queue Cleaner
  decluttarr:
    image: hotio/decluttarr:latest
    container_name: decluttarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - DECLUTTARR_CRON="*/15 * * * *" 
      # API Keys/URLs for Sonarr, Radarr, etc. go here post-setup!
    volumes:
      - ./decluttarr/config:/config
    restart: unless-stopped
    networks:
      - internal-proxy

# Whisparr - Adult # Optional, niche
  whisparr:
    image: ghcr.io/whisparr/whisparr:latest
    container_name: whisparr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./whisparr/config:/config
      - ${ROOT_MEDIA_PATH}:/data
    ports:
      - 6969:6969
    restart: unless-stopped
    networks:
      - internal-proxy
	  
  # Dozzle - Real-Time Container Log Viewer
  dozzle:
    image: amir20/dozzle:latest
    container_name: dozzle
    volumes:
      # CRITICAL: Mounts the Docker socket to read logs from ALL other containers
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 8081:8080 # Using 8081 to avoid conflict with qBittorrent's 8080 UI
    restart: always
    # Note: Dozzle does not need to be on the internal-proxy network.

 

 

 Gluetun

Source:

ThomasWildeTech

Arr

stack

docker

compose

 file