From 62391e4aae43b3c90e89919ccb28b1fd20617212 Mon Sep 17 00:00:00 2001 From: marc Date: Wed, 2 Sep 2026 16:13:46 +0200 Subject: [PATCH] feat(docker): pull-based prod stack (docker-compose.prod.yml + Registry-Images) Produktivsystem baut nicht mehr selbst, sondern zieht die drei fertigen Images (novasonic-frontend/-backend/-scrobble) aus der Gitea-Registry. Das bestehende docker-compose.yml bleibt der Build-auf-dem-Host-Weg. Dazu .dockerignore fuers Frontend-Image (android/, backend/, .git raus) und README/CLAUDE.md aktualisiert. Co-Authored-By: Claude Opus 5 --- .dockerignore | 11 +++++++ CLAUDE.md | 10 +++++-- README.md | 16 ++++++++-- docker-compose.prod.yml | 65 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 .dockerignore create mode 100644 docker-compose.prod.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b31cbb7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +# Frontend build context (root Dockerfile) — keep it to what `vite build` needs. +.git +node_modules +dist +android +docs +scripts +backend +scrobble +.env +*.log diff --git a/CLAUDE.md b/CLAUDE.md index 7c5bacb..ce0276b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,8 +46,14 @@ Backend runs in Docker via **`docker-compose.yml`**, which **builds locally from baked into the images, so container **restarts are instant** and a rebuild after `git pull` is fast (Docker caches the dep layers — only the code COPY layer changes). Deploy: `git pull && docker compose up -d --build`; a plain restart is `docker compose restart novasonic-backend` (the backend also -re-pulls the latest yt-dlp on each start). No registry needed — an optional pull-from-registry / CI -flow can build the same Dockerfiles elsewhere and push (`scripts/build-push-images.mjs`, `.env.example`). +re-pulls the latest yt-dlp on each start). No registry needed for that path. + +**Produktivsystem zieht stattdessen fertige Images** — `docker-compose.prod.yml` referenziert +`${REGISTRY}/novasonic-frontend|-backend|-scrobble:${TAG:-latest}` und baut nichts: +`docker compose -f docker-compose.prod.yml pull && … up -d`. Gepusht wird von der Dev-Maschine mit +`npm run push-images` (`scripts/build-push-images.mjs`, liest `REGISTRY`/`TAG` aus `.env`); ohne Node +tut es direkt `docker build -t $REGISTRY/:latest ` + `docker push` mit den Kontexten +`.`, `backend/`, `scrobble/`. Registry ist die Gitea-Registry (`…/docker`, Owner klein geschrieben). On the deploy host the music lives at `/opt/navidrome/music`. diff --git a/README.md b/README.md index 62d8c00..05b5b99 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ NovaSonic/ ├── vite.config.js ├── Dockerfile ├── docker-compose.yml +├── docker-compose.prod.yml # Produktiv: zieht fertige Images aus der Registry ├── nginx.conf └── docker-entrypoint.sh # Injects NAVIDROME_URL / BACKEND_URL / SCROBBLE_URL at runtime ``` @@ -127,8 +128,19 @@ docker compose up -d --build ``` Builds locally from the Dockerfiles (deps baked in → instant restarts; rebuilds after a `git pull` -are fast thanks to Docker's layer cache). No registry needed. (An optional pull-from-registry / CI -flow exists too — `npm run push-images` + `REGISTRY`/`TAG` in `.env`, see `.env.example`.) +are fast thanks to Docker's layer cache). No registry needed. + +**Produktiv ohne Bauen** — die drei Images kommen fertig aus der Registry: + +```bash +docker compose -f docker-compose.prod.yml pull +docker compose -f docker-compose.prod.yml up -d +``` + +Dafür in der `.env` `REGISTRY` (und optional `TAG`) setzen; die Images heißen +`$REGISTRY/novasonic-frontend|-backend|-scrobble`. Neue Stände von der Dev-Maschine aus mit +`npm run push-images` (bzw. `docker build -t $REGISTRY/:latest && docker push …`) +hochladen — Kontexte: `.` (frontend), `backend/`, `scrobble/`. Frontend on port 8080, backend on port 3700, scrobble service on port 3710. Route each through your reverse proxy. **Important:** the scrobble subdomain must *not* sit behind an SSO / forward-auth gateway — it authenticates itself via Navidrome (`ping.view`), and a gateway in front redirects the app's API calls to a login page. diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..93ff685 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,65 @@ +# NovaSonic — PRODUKTIV: zieht die fertigen Images aus der Registry, baut nichts. +# Auf dem Server nur .env danebenlegen (siehe .env.example) und: +# +# docker compose -f docker-compose.prod.yml pull +# docker compose -f docker-compose.prod.yml up -d +# +# Die Images kommen von der Dev-Maschine: `npm run push-images` +# (baut die drei Dockerfiles und pusht nach ${REGISTRY}). Zum Bauen auf dem Host +# stattdessen das normale docker-compose.yml nehmen. +services: + + # ── Frontend (nginx serving the built PWA) ─────────────────────────────────── + novasonic-frontend: + image: ${REGISTRY}/novasonic-frontend:${TAG:-latest} + container_name: novasonic-frontend + restart: unless-stopped + ports: + - "8080:80" # route this port through your reverse proxy + environment: + - NAVIDROME_URL=${NAVIDROME_URL} # pre-filled on the setup screen + - BACKEND_URL=${BACKEND_URL} + - SCROBBLE_URL=${SCROBBLE_URL} + + # ── Backend (yt-dlp download server) ───────────────────────────────────────── + novasonic-backend: + image: ${REGISTRY}/novasonic-backend:${TAG:-latest} + container_name: novasonic-backend + restart: unless-stopped + volumes: + - ${MUSIC_PATH}:${MUSIC_PATH} # exact path from .env, no remapping + - novasonic-data:/data # writable data dir for overrides, state, etc. + ports: + - "3700:3700" + environment: + - PORT=3700 + - MUSIC_PATH=${MUSIC_PATH} + - DATA_PATH=/data + - AUDIO_FMT=${AUDIO_FMT:-mp3} + - AUDIO_QUALITY=${AUDIO_QUALITY:-0} + - MAX_PARALLEL=${MAX_PARALLEL:-2} + - DOWNLOAD_BACKEND=${DOWNLOAD_BACKEND:-ytdlp} + - TICKETMASTER_API_KEY=${TICKETMASTER_API_KEY} + # Android APK download button — backend proxies the fixed Gitea release ("apk-latest"). + - GITEA_URL=${GITEA_URL:-} + - GITEA_REPO=${GITEA_REPO:-} + - GITEA_TOKEN=${GITEA_TOKEN:-} + - GITEA_APK_TAG=${GITEA_APK_TAG:-apk-latest} + + # ── Scrobble service (isolated timestamped play-log → stats + Wrapped) ─────── + novasonic-scrobble: + image: ${REGISTRY}/novasonic-scrobble:${TAG:-latest} + container_name: novasonic-scrobble + restart: unless-stopped + volumes: + - novasonic-scrobble-data:/data # own DB volume, isolated from downloads + ports: + - "3710:3710" + environment: + - PORT=3710 + - DATA_PATH=/data + - NAVIDROME_URL=${NAVIDROME_URL} # for ping.view auth verification + +volumes: + novasonic-data: + novasonic-scrobble-data: