# Docker Compose für Rippy # # Geräte-Zugriff (22.07./23.07.-Lehre): Optische Laufwerke MÜSSEN als devices: # eingebunden werden. Bind-Mounts unter volumes: geben dem Container zwar den # Geräteknoten, aber KEINE Berechtigung im Device-Cgroup → jedes open() scheitert # mit EPERM. # # ⚠️ ABER NICHT HIER (Vorfall 28.08.2026): Ein `devices:`-Eintrag ist eine # STARTBEDINGUNG. Als das USB-Laufwerk von der VM abgezogen wurde, legte ein # ganz normaler Deploy api, worker UND ui still — obwohl keiner der drei zum # Starten ein Laufwerk braucht: # # Error response from daemon: error gathering device information while # adding custom device "/dev/sr0": no such file or directory # # Der Widerspruch war älter: install.sh sagt bei fehlendem Laufwerk # ausdrücklich „läuft trotzdem durch, dann ist das eine reine # KOMPRIMIER-Maschine" — diese Datei sah das anders. # # Deshalb stehen die Geräte jetzt in docker-compose.override.yml, erzeugt von # `deploy/geraete-override.sh`. Compose zieht die Override von allein dazu; # `docker compose up -d` bleibt derselbe Befehl. Nach jedem An- oder Abstecken # eines Laufwerks das Skript erneut aufrufen. services: api: build: context: . dockerfile: docker/api/Dockerfile environment: - DATABASE_URL=postgresql://rippy:${POSTGRES_PASSWORD:-rippy}@postgres:5432/rippy - REDIS_URL=redis://redis:6379/0 - TMDB_API_KEY=${TMDB_API_KEY:-} - THETVDB_API_KEY=${THETVDB_API_KEY:-} - OMDB_API_KEY=${OMDB_API_KEY:-} # Dasselbe Host-Verzeichnis wie beim Worker, nur an neutraler Stelle: # die API liest den KEYDB-Status und die AACS-Dumps und nimmt eine # hochgeladene KEYDB.cfg entgegen (Einstellungen → System). - MAKEMKV_DATA_DIR=/app/makemkv-data - LOG_LEVEL=INFO - RIPPY_VERSION=${RIPPY_VERSION:-dev} healthcheck: test: ["CMD-SHELL", "python -c 'import urllib.request; urllib.request.urlopen(\"http://localhost:8000/health\")'"] interval: 30s timeout: 10s retries: 3 start_period: 10s ports: - "8000:8000" # SYS_ADMIN: die API hängt Netzwerk-Speicherziele (NFS/SMB) selbst ein # (mounts.py) — dank rshared-Propagation unten sehen Host UND Worker # jeden Mount sofort. # DAC_READ_SEARCH: mount.cifs hebt diese Capability an (toggle_dac_ # capability) — sie fehlt in Dockers Default-Set, ohne sie stirbt JEDER # SMB-Mount mit "Unable to apply new capability set" (Befund 24.07., # auf der VM reproduziert und mit dieser Capability bewiesen behoben). cap_add: - SYS_ADMIN - DAC_READ_SEARCH security_opt: - apparmor:unconfined volumes: # /srv/rippy/media auf der VM statt anonymem Volume: rshared lässt # Mounts aus DIESEM Container zum Host und in andere Container # propagieren (Commander-Anforderung 23.07.: Ziele frei wählbar). - type: bind source: /srv/rippy/media target: /app/media bind: propagation: rshared - temp:/app/temp # MakeMKV-Datenverzeichnis (dasselbe wie im Worker, siehe dort). - ${MAKEMKV_DATA_HOST:-/srv/rippy/makemkv}:/app/makemkv-data # devices: siehe Kopf — kommen aus docker-compose.override.yml networks: - rippy-net depends_on: postgres: condition: service_healthy redis: condition: service_healthy restart: unless-stopped read_only: false worker: build: context: . dockerfile: docker/worker/Dockerfile args: # Übersteuerbar via .env. /download = aktuelle Version, # /download/old = ältere. Am 25.07.2026 nachgemessen: /download # antwortet mit 200, /download/old mit 525 (Cloudflare), und ein # web.archive.org-Schnappschuss mit 404 — deshalb steht hier der # Standard, der wirklich liefert. MAKEMKV_URL_BASE: ${MAKEMKV_URL_BASE:-https://www.makemkv.com/download} # Zweite Quelle, die bei Fehlschlag der ersten automatisch probiert wird # (leer = keine). Bewusst ohne Vorbelegung: eine Adresse, die nicht # liefert, lässt die Konfiguration gesund aussehen und den Build später # scheitern. Der zuverlässige Weg bleibt docker/worker/vendor/. MAKEMKV_URL_FALLBACK: ${MAKEMKV_URL_FALLBACK:-} # MakeMKV-Update OHNE Code-Änderung: neue Version in die .env, # Tarballs nach docker/worker/vendor/ legen (oder Download klappt), # dann docker compose build worker && docker compose up -d worker. # Einstellungen → System meldet, wenn es eine neuere Version gibt. MAKEMKV_VERSION: ${MAKEMKV_VERSION:-1.18.4} environment: - DATABASE_URL=postgresql://rippy:${POSTGRES_PASSWORD:-rippy}@postgres:5432/rippy - REDIS_URL=redis://redis:6379/0 - RIP_OUTPUT_DIR=/app/media - MAKEMKV_APP_KEY=${MAKEMKV_APP_KEY} # MakeMKVs Datenverzeichnis im Container — fest verdrahtet in MakeMKV # selbst (HOME/.MakeMKV, HOME ist /root). Steht hier, damit Worker-Code # und Mount dieselbe Wahrheit benutzen. - MAKEMKV_DATA_DIR=/root/.MakeMKV # Anzeigename in Einstellungen → Worker (stabil über Rebuilds hinweg; # via .env übersteuerbar, z. B. WORKER_NAME=wohnzimmer-vm) - WORKER_NAME=${WORKER_NAME:-rippy-hauptworker} # Für Serien-Episoden-Matching: der Worker fragt die API nach den # Episoden-Laufzeiten der Staffel (TMDB). - API_URL=http://api:8000 - LOG_LEVEL=INFO - RIPPY_VERSION=${RIPPY_VERSION:-dev} volumes: - type: bind source: /srv/rippy/media target: /app/media bind: propagation: rslave - temp:/app/temp # MakeMKV-Datenverzeichnis PERSISTENT (Befund 25.07.2026, live gemessen): # Hier liegen die KEYDB.cfg — heute die EINZIGE funktionierende # Schlüsselquelle für 4K-UHD, weil MakeMKVs Online-Kanal nichts mehr # liefert —, die AACS-Dumps fehlgeschlagener Discs und settings.conf. # Ohne diesen Mount löschte JEDER `up -d --build` beides; die # Fehlermeldung schickte den Nutzer zu einer Datei, die es nicht mehr gab. - ${MAKEMKV_DATA_HOST:-/srv/rippy/makemkv}:/root/.MakeMKV # devices: siehe Kopf dieser Datei — die optischen Knoten dieses Hosts # stehen in docker-compose.override.yml (deploy/geraete-override.sh). # MakeMKV braucht BEIDE: /dev/srN und den passenden /dev/sgM; welche # sg-Nummer dazugehoert, ist je Host anders und wird dort ueber die # SCSI-Adresse abgeglichen statt geraten. networks: - rippy-net depends_on: postgres: condition: service_healthy redis: condition: service_healthy restart: unless-stopped # read_only bleibt Ziel (Etappe 6), aber MakeMKV/abcde brauchen HOME + # Settings — bis zur Härtung schreibbar, Ausgabe geht ohnehin aufs Volume. read_only: false tmpfs: - /app/tmp - /run - /tmp ui: build: context: . dockerfile: docker/ui/Dockerfile ports: - "80:80" networks: - rippy-net depends_on: - api restart: unless-stopped postgres: image: postgres:16-alpine environment: - POSTGRES_USER=rippy - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-rippy} - POSTGRES_DB=rippy # Veröffentlicht (Befund 24.07.): Remote-Worker (GPU-Maschine, künftiger # Windows-Worker) verbinden sich über :5432/6379 — ohne # ports: konnte sich NIE ein Remote-Worker anbinden. Heimnetz-Kompromiss, # wie die Klartext-Credentials (README). ports: - "5432:5432" volumes: - postgres-data:/var/lib/postgresql/data networks: - rippy-net restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U rippy -d rippy"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine # Siehe postgres: Pflicht für Remote-Worker (Celery-Broker). ports: - "6379:6379" volumes: - redis-data:/data networks: - rippy-net restart: unless-stopped command: redis-server --appendonly yes healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 networks: rippy-net: driver: bridge volumes: temp: postgres-data: redis-data: