Files
Ainulindale/docker-compose.yml
2026-09-18 18:13:05 +02:00

177 lines
7.1 KiB
YAML

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