services: keycloak: image: quay.io/keycloak/keycloak:26.2 command: start-dev --import-realm volumes: - ./keycloak-realm.json:/opt/keycloak/data/import/realm.json:ro ports: - "8080:8080" environment: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin KC_HOSTNAME: localhost KC_HOSTNAME_PORT: "8080" KC_HTTP_ENABLED: "true" KC_HOSTNAME_STRICT: "false" healthcheck: test: ["CMD-SHELL", "cat < /dev/null > /dev/tcp/localhost/8080"] interval: 5s timeout: 3s retries: 30 start_period: 30s wiremock-arr: image: wiremock/wiremock:3.10.0 network_mode: host command: ["--port", "9100", "--disable-banner"] healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:9100/__admin/health || exit 1"] interval: 5s timeout: 3s retries: 30 start_period: 10s wiremock-dlc: image: wiremock/wiremock:3.10.0 network_mode: host command: ["--port", "9200", "--disable-banner"] healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:9200/__admin/health || exit 1"] interval: 5s timeout: 3s retries: 30 start_period: 10s wiremock-notify: image: wiremock/wiremock:3.10.0 network_mode: host command: ["--port", "9300", "--disable-banner"] healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:9300/__admin/health || exit 1"] interval: 5s timeout: 3s retries: 30 start_period: 10s wiremock-blocklist: image: wiremock/wiremock:3.10.0 network_mode: host command: ["--port", "9400", "--disable-banner"] healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:9400/__admin/health || exit 1"] interval: 5s timeout: 3s retries: 30 start_period: 10s # The fake Torznab indexer the real Sonarr and Radarr search against. # Its caps and probe feed are file-based mappings, which survive a resetAll. # Tests add higher-priority stubs for the releases they want returned. wiremock-indexer: image: wiremock/wiremock:3.10.0 network_mode: host volumes: - ./wiremock-indexer:/home/wiremock command: ["--port", "9500", "--disable-banner"] healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:9500/__admin/health || exit 1"] interval: 5s timeout: 3s retries: 30 start_period: 10s app: # CI loads a prebuilt image under this name so no leg has to build one. image: ${APP_IMAGE:-cleanuparr-e2e-app} build: context: ../code args: PACKAGES_USERNAME: ${PACKAGES_USERNAME} secrets: - packages_pat network_mode: host depends_on: keycloak: condition: service_healthy wiremock-arr: condition: service_started wiremock-dlc: condition: service_started wiremock-notify: condition: service_started wiremock-blocklist: condition: service_started extra_hosts: - "host.docker.internal:host-gateway" environment: PORT: "5000" HTTP_PORTS: "5000" # Ephemeral /config — wiped on every container restart. The e2e harness # restarts the app container between spec folders for a clean baseline. tmpfs: - /config volumes: - ./test-data/downloads:/e2e-downloads # This is a different bind mount on purpose. A rename operation between # two mounts fails with the EXDEV error. The cross-device orphaned-files # test uses this behavior. - ./test-data/orphaned-xdev:/e2e-orphaned nginx: image: nginx:1.27-alpine network_mode: host volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - app tracker: image: wiltonsr/opentracker:open network_mode: host restart: unless-stopped qbittorrent: image: lscr.io/linuxserver/qbittorrent:5.2.3 network_mode: host environment: PUID: "1000" PGID: "1000" TZ: "UTC" WEBUI_PORT: "8090" TORRENTING_PORT: "6881" volumes: - ./test-data/qbittorrent-config:/config - ./test-data/downloads/qbittorrent:/downloads healthcheck: test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:8090/api/v2/app/version > /dev/null 2>&1 || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 30s transmission: image: lscr.io/linuxserver/transmission:4.0.6 network_mode: host environment: PUID: "1000" PGID: "1000" TZ: "UTC" USER: "transmission" PASS: "transmission" PEERPORT: "51413" volumes: - ./test-data/transmission-config:/config - ./test-data/downloads/transmission:/downloads healthcheck: test: ["CMD-SHELL", "curl -s -o /dev/null http://localhost:9091/transmission/rpc || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 30s deluge: image: lscr.io/linuxserver/deluge:2.1.1 network_mode: host environment: PUID: "1000" PGID: "1000" TZ: "UTC" DELUGE_LOGLEVEL: "info" volumes: - ./test-data/deluge-config:/config - ./test-data/downloads/deluge:/downloads healthcheck: test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:8112 > /dev/null 2>&1 || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 30s utorrent: image: ekho/utorrent:latest platform: linux/amd64 ports: - "8083:8080" extra_hosts: - "host.docker.internal:host-gateway" environment: UID: "1000" GID: "1000" dir_root: "/downloads" dir_active: "/downloads" dir_completed: "/downloads" dir_download: "/downloads" volumes: - ./test-data/utorrent-config:/data - ./test-data/downloads/utorrent:/downloads healthcheck: test: ["CMD-SHELL", "curl -s -o /dev/null http://localhost:8080/gui/ || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 30s # Real Sonarr and Radarr, restored from e2e/arr-seed by setup-test-data.sh. # Every run starts with the same library, API key, indexer and download client. # Regenerate the seed with `make seed-arr`. # The tags are pinned: a seed database is only valid for the version that wrote it. sonarr: image: lscr.io/linuxserver/sonarr:4.0.19 network_mode: host environment: PUID: "1000" PGID: "1000" TZ: "UTC" volumes: - ./test-data/sonarr-config:/config - ./test-data/sonarr-tv:/tv healthcheck: test: ["CMD-SHELL", "curl -fs -o /dev/null http://localhost:8989/ping || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 30s radarr: image: lscr.io/linuxserver/radarr:6.3.0 network_mode: host environment: PUID: "1000" PGID: "1000" TZ: "UTC" volumes: - ./test-data/radarr-config:/config - ./test-data/radarr-movies:/movies healthcheck: test: ["CMD-SHELL", "curl -fs -o /dev/null http://localhost:7878/ping || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 30s # Real LazyLibrarian, restored from e2e/lazylibrarian-seed by setup-test-data.sh. # LazyLibrarian publishes no versions, so the tag names the upstream commit. # The seed database is only valid for the commit that wrote it. # Regenerate the seed with `make seed-lazylibrarian`. lazylibrarian: image: lscr.io/linuxserver/lazylibrarian:276d79c1-ls333 network_mode: host environment: PUID: "1000" PGID: "1000" TZ: "UTC" volumes: - ./test-data/lazylibrarian-config:/config - ./test-data/downloads/qbittorrent:/downloads - ./test-data/lazylibrarian-books:/books healthcheck: test: ["CMD-SHELL", "curl -fs -o /dev/null http://localhost:5299/ || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 30s rutorrent: image: lscr.io/linuxserver/rutorrent:latest ports: - "8088:80" environment: PUID: "1000" PGID: "1000" TZ: "UTC" volumes: - ./test-data/rutorrent-config:/config - ./test-data/downloads/rtorrent:/downloads healthcheck: test: ["CMD-SHELL", "curl -fs -o /dev/null http://localhost || exit 1"] interval: 5s timeout: 3s retries: 12 start_period: 60s secrets: packages_pat: environment: PACKAGES_PAT