services: # --------------------------------------------------------------------------- # VPN gateway. slskd lives inside this container's network namespace. # --------------------------------------------------------------------------- gluetun: image: qmcgaw/gluetun:v3.41.3 container_name: gluetun cap_add: - NET_ADMIN devices: - /dev/net/tun:/dev/net/tun environment: VPN_SERVICE_PROVIDER: protonvpn VPN_TYPE: wireguard WIREGUARD_PRIVATE_KEY: ${WIREGUARD_PRIVATE_KEY} WIREGUARD_ADDRESSES: ${WIREGUARD_ADDRESSES} SERVER_COUNTRIES: ${VPN_COUNTRY:-Switzerland} TZ: ${TZ:-Europe/Zurich} # One switch for both containers. Must be true/false: gluetun also accepts # on/off, but slskd reads the same value and only understands true/false. VPN_PORT_FORWARDING: ${PORT_FORWARDING:-false} VPN_PORT_FORWARDING_PROVIDER: protonvpn PORT_FORWARD_ONLY: ${PORT_FORWARDING:-false} # Control server on :8000. slskd polls it (over localhost, they share a # network namespace) to follow the tunnel state and the forwarded port. # Every route needs this key; the port is deliberately NOT published. HTTP_CONTROL_SERVER_AUTH_DEFAULT_ROLE: '{"auth":"apikey","apikey":"${GLUETUN_API_KEY}"}' ports: - 5030:5030 # slskd web UI volumes: - ${APPDATA_PATH}/gluetun:/gluetun restart: unless-stopped labels: - autoheal=true # --------------------------------------------------------------------------- # Soulseek client # --------------------------------------------------------------------------- slskd: image: slskd/slskd:0.26.0 container_name: slskd network_mode: "service:gluetun" depends_on: gluetun: condition: service_healthy restart: true # compose restarts slskd whenever it restarts gluetun environment: SLSKD_REMOTE_CONFIGURATION: "true" SLSKD_SLSK_USERNAME: ${SOULSEEK_USERNAME} SLSKD_SLSK_PASSWORD: ${SOULSEEK_PASSWORD} SLSKD_USERNAME: ${SLSKD_WEB_USERNAME} SLSKD_PASSWORD: ${SLSKD_WEB_PASSWORD} SLSKD_API_KEY: ${SLSKD_API_KEY} # used by Explo, the orchestrator and the healthcheck SLSKD_DOWNLOADS_DIR: /downloads SLSKD_INCOMPLETE_DIR: /downloads/incomplete # Native gluetun integration: slskd waits for the tunnel before logging in, # drops the Soulseek session when the tunnel drops, logs back in when it # returns, and (with port forwarding) applies the new listen port itself. SLSKD_VPN: "true" SLSKD_VPN_PORT_FORWARDING: ${PORT_FORWARDING:-false} SLSKD_VPN_GLUETUN_URL: http://localhost:8000 SLSKD_VPN_GLUETUN_API_KEY: ${GLUETUN_API_KEY} volumes: - ${APPDATA_PATH}/slskd:/app - ${SLSKD_DOWNLOADS_PATH:-${MUSIC_PATH}/slskd}:/downloads - ${MUSIC_PATH}:/music:ro - ./slskd/healthcheck.sh:/healthcheck.sh:ro healthcheck: # The image's own check only looks at the web server and has a 60 minute # start period. This one follows the Soulseek login (see the script). test: ["CMD", "bash", "/healthcheck.sh"] interval: 60s timeout: 20s retries: 3 start_period: 3m restart: unless-stopped labels: - autoheal=true # --------------------------------------------------------------------------- # Restarts anything labelled autoheal=true once Docker marks it unhealthy # --------------------------------------------------------------------------- autoheal: image: willfarrell/autoheal:latest container_name: autoheal restart: unless-stopped environment: AUTOHEAL_CONTAINER_LABEL: autoheal AUTOHEAL_INTERVAL: 30 AUTOHEAL_START_PERIOD: 120 AUTOHEAL_DEFAULT_STOP_TIMEOUT: 30 TZ: ${TZ:-Europe/Zurich} volumes: - /var/run/docker.sock:/var/run/docker.sock # --------------------------------------------------------------------------- # Discovery: the Explo image, driven by discovery/discover.py # --------------------------------------------------------------------------- explo: image: ghcr.io/lumepart/explo:v1.2.0 # discover.py writes Explo's v1.2 playlist cache; re-test before bumping container_name: explo entrypoint: ["/bin/sh", "/ainulindale/entrypoint.sh"] environment: TZ: ${TZ:-Europe/Zurich} # PUID: ${PUID:-99} # opt-in: run as a normal user instead of root. # PGID: ${PGID:-100} # chown $APPDATA_PATH/explo and $MUSIC_PATH/explo first. # --- schedule ----------------------------------------------------------- DISCOVERY_SCHEDULE: ${DISCOVERY_SCHEDULE:-15 0 * * *} RUN_ON_START: ${RUN_ON_START:-false} EXPLO_WEB_UI: ${EXPLO_WEB_UI:-false} # --- sources ------------------------------------------------------------ DISCOVERY_SERVICE: listenbrainz LISTENBRAINZ_DISCOVERY: playlist LISTENBRAINZ_USER: ${LISTENBRAINZ_USER} LISTENBRAINZ_USER_TOKEN: ${LISTENBRAINZ_USER_TOKEN:-} LASTFM_USER: ${LASTFM_USER:-} LASTFM_API_KEY: ${LASTFM_API_KEY:-} LASTFM_MODE: ${LASTFM_MODE:-always} LASTFM_STATIONS: ${LASTFM_STATIONS:-recommended} LASTFM_TRACKS: ${LASTFM_TRACKS:-30} # --- retention ---------------------------------------------------------- KEEP_WEEKS: ${KEEP_WEEKS:-2} PRUNE_FILES: ${PRUNE_FILES:-true} KEEP_FAVOURITES: ${KEEP_FAVOURITES:-true} PRUNE_LEGACY_JAMS: ${PRUNE_LEGACY_JAMS:-false} # --- Navidrome ---------------------------------------------------------- EXPLO_SYSTEM: subsonic SYSTEM_URL: ${NAVIDROME_URL} SYSTEM_USERNAME: ${NAVIDROME_USERNAME} SYSTEM_PASSWORD: ${NAVIDROME_PASSWORD} PLAYLISTNAME_FORMAT: week # the pruner recognises playlists by this naming USE_SUBDIRECTORY: "true" # one folder per playlist, which is what gets pruned # --- downloading -------------------------------------------------------- DOWNLOAD_SERVICES: ${DOWNLOAD_SERVICES:-slskd,youtube} SLSKD_URL: http://gluetun:5030 # slskd has no address of its own, it answers on gluetun's SLSKD_API_KEY: ${SLSKD_API_KEY} SLSKD_MIGRATE_DOWNLOADS: "true" REQUIRE_SLSKD: ${REQUIRE_SLSKD:-true} EXTENSIONS: ${EXTENSIONS:-flac,wav,mp3} MIN_BITRATE: ${MIN_BITRATE:-320} MIN_BIT_DEPTH: ${MIN_BIT_DEPTH:-16} TRACK_EXTENSION: mp3 YOUTUBE_API_KEY: ${YOUTUBE_API_KEY:-} SLEEP: 2 LOG_LEVEL: ${LOG_LEVEL:-INFO} volumes: - ./discovery:/ainulindale:ro - ${APPDATA_PATH}/explo/config:/opt/explo/config # playlist cache + orchestrator state, must persist - ${MUSIC_PATH}/explo:/data - ${SLSKD_DOWNLOADS_PATH:-${MUSIC_PATH}/slskd}:/slskd # ports: # - 7288:7288 # only with EXPLO_WEB_UI=true healthcheck: test: ["CMD-SHELL", "pgrep crond >/dev/null"] interval: 5m timeout: 5s restart: unless-stopped lidarr: image: lscr.io/linuxserver/lidarr:latest container_name: lidarr environment: PUID: ${PUID:-99} PGID: ${PGID:-100} TZ: ${TZ:-Europe/Zurich} volumes: - ${APPDATA_PATH}/lidarr:/config - ${MUSIC_PATH}:/music ports: - 8686:8686 restart: unless-stopped