Ctrl K
ComunidadGuías…

docker-compose.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
services:
  gluetun:
    image: qmcgaw/gluetun
    container_name: gluetun
    labels:
    - com.centurylinklabs.watchtower.enable=false
    cap_add:
    - NET_ADMIN
    devices:
    - /dev/net/tun:/dev/net/tun
    # Ports bind to ALL interfaces so Tailnet clients (tailscale0) reach the admin UIs as well as the
    # LAN; binding to MEDIA_LAN_IP only breaks Tailscale access. The perimeter relies on UFW off +
    # Tailnet Lock on, no public-routable host interface, and admin UI auth where configured. On a
    # host with a public interface, add `${MEDIA_LAN_IP}:HOST:CONTAINER` bindings (and one for the
    # Tailscale IP).
    ports:
    - 8090:8090
    # No static peer-port publish: inbound peers arrive on the port Proton allocates, which gluetun
    # opens in its own firewall.
    - 9696:9696
    - 8989:8989
    - 7878:7878
    - 6767:6767
    - 8192:8191
    dns:
    - 8.8.8.8
    - 1.1.1.1
    environment:
    - VPN_SERVICE_PROVIDER=protonvpn
    - VPN_TYPE=wireguard
    - WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY:?WIREGUARD_PRIVATE_KEY missing — see .env.example}
    - WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESS:?WIREGUARD_ADDRESS missing — see .env.example}
    - SERVER_COUNTRIES=${VPN_COUNTRY:-Switzerland}
    # Health checks target IPs, not hostnames: gluetun's default targets resolve through its OWN DNS
    # at 127.0.0.1:53, so a resolver hiccup fails liveness while the tunnel is healthy. Port 443 still
    # does a full TLS handshake, so it stays a real end-to-end check, and tunnel enforcement is
    # unaffected (both IPs route via tun0, OUTPUT defaults to DROP, neither is inside
    # FIREWALL_OUTBOUND_SUBNETS). The permanent-unhealthy state is believed to be an upstream
    # health-checker deadlock (HEALTH_RESTART_VPN never fires again), fixed in v3.41.1. Watchtower
    # stays disabled for gluetun (label above): a restart drops the netns for seven containers.
    - HEALTH_TARGET_ADDRESSES=1.1.1.1:443,8.8.8.8:443
    # ProtonVPN port forwarding (without it qBittorrent is outbound-only). PORT_FORWARD_ONLY is
    # REQUIRED: Proton forwards only on its P2P servers and SERVER_COUNTRIES alone does not prefer
    # them. A NAT-PMP-ENABLED WIREGUARD KEY IS ALSO REQUIRED (Proton hands the port out over NAT-PMP,
    # udp/5351): generate it with NAT-PMP on and Moderate NAT off, on a Standard (not Secure Core)
    # server, then RECREATE gluetun, which does not retry a failed allocation. The port is DYNAMIC, so
    # no static port belongs anywhere in this file; sync-forwarded-port.py reconciles qBittorrent's
    # listen port on a timer.
    - VPN_PORT_FORWARDING=on
    - PORT_FORWARD_ONLY=on
    # gluetun's default :::8000 control server is reachable on the container IP and the auth file
    # grants GET /v1/portforward with auth = "none", so pin it to loopback.
    - HTTP_CONTROL_SERVER_ADDRESS=127.0.0.1:8000
    # LAN + Tailnet CGNAT range (100.64.0.0/10). Inbound to published ports works via conntrack, but
    # containers in gluetun's namespace that initiate outbound to Tailnet-only services (e.g. host
    # webhooks via 100.x) need this allowlist. Override the LAN part via .env.
    - FIREWALL_OUTBOUND_SUBNETS=${FIREWALL_OUTBOUND_SUBNETS:-10.0.0.0/24,100.64.0.0/10}
    # Grants the one control-server route sync-forwarded-port.py polls; /gluetun/auth/config.toml is
    # gluetun's default path.
    volumes:
    - ./ops/gluetun/auth-config.toml:/gluetun/auth/config.toml:ro
    restart: unless-stopped
    networks:
    - internal-media
  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent-dvr
    network_mode: service:gluetun
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    - WEBUI_PORT=8090
    volumes:
    - ./config/qbittorrent:/config
    - ./data/torrents:/downloads
    restart: unless-stopped
    depends_on:
    - gluetun
  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr-dvr
    network_mode: service:gluetun
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    volumes:
    - ./config/prowlarr:/config
    restart: unless-stopped
    depends_on:
    - gluetun
  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    network_mode: service:gluetun
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    volumes:
    - ./config/sonarr:/config
    - ${MEDIA_ROOT:-${MEDIA_STACK_ROOT:-$HOME/media-stack}/data/media}/tv:/tv
    - ./data/torrents:/downloads
    restart: unless-stopped
    depends_on:
    - gluetun
  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    network_mode: service:gluetun
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    volumes:
    - ./config/radarr:/config
    - ${MEDIA_ROOT:-${MEDIA_STACK_ROOT:-$HOME/media-stack}/data/media}/movies:/movies
    - ./data/torrents:/downloads
    restart: unless-stopped
    depends_on:
    - gluetun
  bazarr:
    image: lscr.io/linuxserver/bazarr:latest
    container_name: bazarr
    network_mode: service:gluetun
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    volumes:
    - ./config/bazarr:/config
    - ${MEDIA_ROOT:-${MEDIA_STACK_ROOT:-$HOME/media-stack}/data/media}/tv:/tv
    - ${MEDIA_ROOT:-${MEDIA_STACK_ROOT:-$HOME/media-stack}/data/media}/movies:/movies
    restart: unless-stopped
    depends_on:
    - gluetun
  flaresolverr:
    image: ghcr.io/flaresolverr/flaresolverr:latest
    container_name: flaresolverr-dvr
    network_mode: service:gluetun
    environment:
    - LOG_LEVEL=info
    - TZ=${TZ:-UTC}
    restart: unless-stopped
    depends_on:
    - gluetun
  unpackerr:
    image: golift/unpackerr
    container_name: unpackerr
    environment:
    - UN_SONARR_0_URL=http://localhost:8989
    - UN_SONARR_0_API_KEY=${SONARR_API_KEY:?SONARR_API_KEY missing — see .env.example}
    - UN_RADARR_0_URL=http://localhost:7878
    - UN_RADARR_0_API_KEY=${RADARR_API_KEY:?RADARR_API_KEY missing — see .env.example}
    volumes:
    - ./data/torrents:/downloads
    restart: unless-stopped
    network_mode: service:gluetun
    depends_on:
    - gluetun
  watchtower:
    # nickfedor/watchtower is the maintained fork: upstream containrrr is abandoned and its Docker API
    # client (1.25) is below the daemon's 1.44 minimum.
    image: nickfedor/watchtower:latest
    container_name: watchtower
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    environment:
    - WATCHTOWER_CLEANUP=true
    - WATCHTOWER_POLL_INTERVAL=86400
    restart: unless-stopped
    networks:
    - internal-media
  tdarr:
    container_name: tdarr
    image: ghcr.io/haveagitgat/tdarr:latest
    restart: unless-stopped
    ports:
    - 8265:8265
    - 8266:8266
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    - serverIP=0.0.0.0
    - disableAnalytics=true
    volumes:
    - ${MEDIA_STACK_ROOT:-$HOME/media-stack}/config/tdarr:/app/server
    - ${MEDIA_STACK_ROOT:-$HOME/media-stack}/config/tdarr_configs:/app/configs
    - ${MEDIA_ROOT:-${MEDIA_STACK_ROOT:-$HOME/media-stack}/data/media}:/media
    networks:
    - internal-media
  tdarr-node:
    container_name: tdarr-node
    image: ghcr.io/haveagitgat/tdarr_node:latest
    restart: unless-stopped
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    - nodeID=Balls3-NVIDIA
    - serverIP=tdarr
    - serverPort=8266
    - disableAnalytics=true
    volumes:
    - ${MEDIA_STACK_ROOT:-$HOME/media-stack}/config/tdarr_node:/app/configs
    - ${MEDIA_ROOT:-${MEDIA_STACK_ROOT:-$HOME/media-stack}/data/media}:/media
    - /tmp:/temp
    runtime: nvidia
    deploy:
      resources:
        reservations:
          devices:
          - driver: nvidia
            count: 1
            capabilities:
            - gpu
    depends_on:
    - tdarr
    networks:
    - internal-media
  jellyfin:
    container_name: jellyfin
    image: jellyfin/jellyfin:latest
    restart: unless-stopped
    ports:
    - 8096:8096
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    # Default to the LAN IP: a placeholder tailnet name would advertise a non-resolvable host.
    - JELLYFIN_PublishedServerUrl=${JELLYFIN_PUBLISHED_URL:-${MEDIA_LAN_IP:-10.0.0.10}}
    volumes:
    - ${MEDIA_STACK_ROOT:-$HOME/media-stack}/config/jellyfin:/config
    - ${MEDIA_ROOT:-${MEDIA_STACK_ROOT:-$HOME/media-stack}/data/media}:/data
    runtime: nvidia
    deploy:
      resources:
        reservations:
          devices:
          - driver: nvidia
            count: 1
            capabilities:
            - gpu
    networks:
    - internal-media
  jellyseerr:
    image: ghcr.io/seerr-team/seerr:latest
    container_name: jellyseerr
    restart: unless-stopped
    networks:
    - internal-media
    ports:
    - 5055:5055
    environment:
    - LOG_LEVEL=debug
    - TZ=${TZ:-UTC}
    volumes:
    - ${MEDIA_STACK_ROOT:-$HOME/media-stack}/config/jellyseerr:/app/config
  # readarr: removed (upstream abandoned); config remains at ./config/readarr.
  kavita:
    image: lscr.io/linuxserver/kavita:latest
    container_name: kavita
    environment:
    - PUID=${PUID:-1000}
    - PGID=${PGID:-1000}
    - TZ=${TZ:-UTC}
    volumes:
    - ./config/kavita:/config
    - ${CALIBRE_LIBRARY:-/srv/calibre/library}:/books
    restart: unless-stopped
    # Direct publish, not via gluetun: a mapping on gluetun would point host:5000 at its empty namespace.
    ports:
    - 5000:5000
    networks:
    - internal-media
networks:
  internal-media:
    driver: bridge