mirror of
https://github.com/Chevron7Locked/kima-hub.git
synced 2026-06-19 07:37:17 +00:00
60892c12e7
Fixes #158 -- fresh docker-compose install shows login instead of setup wizard because Next.js rewrites were compiled with 127.0.0.1:3006 baked in. Added NEXT_PUBLIC_BACKEND_URL as a Dockerfile build arg, defaulting to http://backend:3006 in docker-compose.yml so the frontend container proxies to the backend service correctly. Audiobookshelf sync fixes: - syncAudiobook returns boolean; skipped books no longer count as synced - Sync notification now surfaces failed/skipped counts - downloadCover has 10s fetch timeout (was unbounded) - book.size changed to book.media?.size for audio-only size Added sync button on audiobooks page so users can trigger a sync without going through settings or the enrichment system. Bump to v1.7.2
331 lines
12 KiB
YAML
331 lines
12 KiB
YAML
services:
|
|
# ==============================================================================
|
|
# KIMA CORE APPLICATION
|
|
# ==============================================================================
|
|
|
|
# Backend API - Your Kima Express.js server
|
|
backend:
|
|
build:
|
|
context: ./backend
|
|
dockerfile: Dockerfile
|
|
container_name: kima_backend
|
|
environment:
|
|
# Database
|
|
DATABASE_URL: postgresql://${POSTGRES_USER:-kimadb}:${POSTGRES_PASSWORD:-changeme}@postgres:5432/${POSTGRES_DB:-kima}
|
|
POSTGRES_USER: ${POSTGRES_USER:-kimadb}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
|
|
POSTGRES_DB: ${POSTGRES_DB:-kima}
|
|
REDIS_URL: redis://redis:6379
|
|
|
|
# Server config
|
|
PORT: 3006
|
|
NODE_ENV: ${NODE_ENV:-production}
|
|
SESSION_SECRET: ${SESSION_SECRET:-changeme-generate-secure-key}
|
|
SETTINGS_ENCRYPTION_KEY: ${SETTINGS_ENCRYPTION_KEY:-}
|
|
# To reset admin password, uncomment and set, then restart:
|
|
# ADMIN_RESET_PASSWORD: your-new-password
|
|
|
|
# Music library (required)
|
|
# IMPORTANT: Inside Docker, music is always at /music (mounted from host MUSIC_PATH)
|
|
# Don't use ${MUSIC_PATH} here as it contains the host path, not container path
|
|
MUSIC_PATH: /music
|
|
TRANSCODE_CACHE_PATH: /app/cache/transcodes
|
|
TRANSCODE_CACHE_MAX_GB: ${TRANSCODE_CACHE_MAX_GB:-10}
|
|
|
|
# CORS
|
|
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:3030}
|
|
|
|
# Internal API secret for service-to-service auth
|
|
INTERNAL_API_SECRET: ${INTERNAL_API_SECRET:-kima-internal-secret-change-me}
|
|
|
|
# Lidarr webhook callback URL - how Lidarr can reach Kima
|
|
# Use backend:3006 for same-network communication, or external IP:3030 for external Lidarr
|
|
KIMA_CALLBACK_URL: ${KIMA_CALLBACK_URL:-http://backend:3006}
|
|
# Debug: enable extra podcast stream/cache logging
|
|
PODCAST_DEBUG: ${PODCAST_DEBUG:-0}
|
|
volumes:
|
|
- ${MUSIC_PATH:-./music}:/music
|
|
- backend_cache:/app/cache
|
|
- backend_logs:/app/logs
|
|
ports:
|
|
- "${BACKEND_PORT:-3006}:3006"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "node", "healthcheck.js"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
networks:
|
|
default:
|
|
aliases:
|
|
- backend
|
|
- kima_backend
|
|
|
|
# Frontend Web UI - Your Kima Next.js app
|
|
frontend:
|
|
build:
|
|
context: ./frontend
|
|
dockerfile: Dockerfile
|
|
args:
|
|
# Prefer relative /api requests (Next.js rewrites will proxy to backend).
|
|
# If you need a custom API URL, set NEXT_PUBLIC_API_URL explicitly.
|
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-}
|
|
# Build type: "nightly" for dev builds, unset/empty for tagged releases
|
|
NEXT_PUBLIC_BUILD_TYPE: ${NEXT_PUBLIC_BUILD_TYPE:-nightly}
|
|
# Backend URL for Next.js rewrites (compiled at build time).
|
|
# In multi-container mode, frontend proxies to the backend service.
|
|
NEXT_PUBLIC_BACKEND_URL: ${NEXT_PUBLIC_BACKEND_URL:-http://backend:3006}
|
|
container_name: kima_frontend
|
|
environment:
|
|
NODE_ENV: ${NODE_ENV:-production}
|
|
# Used by next.config.ts rewrites() inside the container.
|
|
# IMPORTANT: "localhost" inside a container refers to itself, not the host.
|
|
BACKEND_URL: ${BACKEND_URL:-http://backend:3006}
|
|
# Exposed to the browser; leave empty by default so the app uses /api via rewrites.
|
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-}
|
|
ports:
|
|
- "${FRONTEND_PORT:-3030}:3030"
|
|
depends_on:
|
|
- backend
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "node", "healthcheck.js"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
# ==============================================================================
|
|
# INFRASTRUCTURE (Database & Cache)
|
|
# ==============================================================================
|
|
|
|
postgres:
|
|
image: pgvector/pgvector:pg16
|
|
container_name: kima_db
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER:-kimadb}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
|
|
POSTGRES_DB: ${POSTGRES_DB:-kima}
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
ports:
|
|
- "${POSTGRES_PORT:-5432}:5432"
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
# IMPORTANT:
|
|
# pg_isready defaults to connecting to a database with the same name as the user.
|
|
# Our default user is "kimadb" but default database is "kima",
|
|
# so without -d this will spam logs with: FATAL: database "kimadb" does not exist
|
|
test:
|
|
[
|
|
"CMD-SHELL",
|
|
"pg_isready -U ${POSTGRES_USER:-kimadb} -d ${POSTGRES_DB:-kima}",
|
|
]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: kima_redis
|
|
ports:
|
|
- "${REDIS_PORT:-6379}:6379"
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# ==============================================================================
|
|
# EXTERNAL SERVICES (Music Management)
|
|
# ==============================================================================
|
|
|
|
# Lidarr - Music collection manager
|
|
lidarr:
|
|
image: lscr.io/linuxserver/lidarr:latest
|
|
container_name: kima_lidarr
|
|
environment:
|
|
- PUID=${PUID:-1000}
|
|
- PGID=${PGID:-1000}
|
|
- TZ=${TZ:-UTC}
|
|
volumes:
|
|
- lidarr_config:/config
|
|
- ${MUSIC_PATH:-./music}:/music
|
|
- ${DOWNLOAD_PATH:-./downloads}:/downloads
|
|
ports:
|
|
- "8686:8686"
|
|
restart: unless-stopped
|
|
|
|
# Prowlarr - Indexer manager
|
|
prowlarr:
|
|
image: lscr.io/linuxserver/prowlarr:latest
|
|
container_name: kima_prowlarr
|
|
environment:
|
|
- PUID=${PUID:-1000}
|
|
- PGID=${PGID:-1000}
|
|
- TZ=${TZ:-UTC}
|
|
volumes:
|
|
- prowlarr_config:/config
|
|
ports:
|
|
- "9696:9696"
|
|
restart: unless-stopped
|
|
|
|
# FlareSolverr - Cloudflare bypass for Prowlarr
|
|
flaresolverr:
|
|
image: ghcr.io/flaresolverr/flaresolverr:latest
|
|
container_name: kima_flaresolverr
|
|
environment:
|
|
- LOG_LEVEL=${LOG_LEVEL:-info}
|
|
- LOG_HTML=${LOG_HTML:-false}
|
|
- CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}
|
|
- TZ=${TZ:-UTC}
|
|
ports:
|
|
- "8191:8191"
|
|
restart: unless-stopped
|
|
|
|
# NOTE: Soulseek is now handled directly by the backend using slsk-client
|
|
# No Docker container needed - downloads go straight to Singles/Artist/Album/
|
|
|
|
# qBittorrent - Torrent client
|
|
qbittorrent:
|
|
image: lscr.io/linuxserver/qbittorrent:latest
|
|
container_name: kima_qbittorrent
|
|
environment:
|
|
- PUID=${PUID:-1000}
|
|
- PGID=${PGID:-1000}
|
|
- TZ=${TZ:-UTC}
|
|
- WEBUI_PORT=8080
|
|
volumes:
|
|
- qbittorrent_config:/config
|
|
- ${MUSIC_PATH:-./music}/torrents:/music/torrents
|
|
- ${DOWNLOAD_PATH:-./downloads}:/downloads
|
|
ports:
|
|
- "8080:8080"
|
|
- "6881:6881"
|
|
- "6881:6881/udp"
|
|
restart: unless-stopped
|
|
|
|
# SABnzbd - Usenet client
|
|
sabnzbd:
|
|
image: lscr.io/linuxserver/sabnzbd:latest
|
|
container_name: kima_sabnzbd
|
|
environment:
|
|
- PUID=${PUID:-1000}
|
|
- PGID=${PGID:-1000}
|
|
- TZ=${TZ:-UTC}
|
|
volumes:
|
|
- sabnzbd_config:/config
|
|
- ${DOWNLOAD_PATH:-./downloads}:/downloads
|
|
ports:
|
|
- "8085:8080"
|
|
restart: unless-stopped
|
|
|
|
# ==============================================================================
|
|
# AUDIO ANALYSIS SERVICE (Essentia)
|
|
# ==============================================================================
|
|
|
|
# Audio Analyzer - Extracts BPM, key, mood, and other audio features
|
|
audio-analyzer:
|
|
build:
|
|
context: ./services/audio-analyzer
|
|
container_name: kima_audio_analyzer
|
|
environment:
|
|
REDIS_URL: redis://redis:6379
|
|
DATABASE_URL: postgresql://${POSTGRES_USER:-kimadb}:${POSTGRES_PASSWORD:-changeme}@postgres:5432/${POSTGRES_DB:-kima}
|
|
MUSIC_PATH: /music
|
|
BATCH_SIZE: ${AUDIO_ANALYSIS_BATCH_SIZE:-10}
|
|
SLEEP_INTERVAL: ${AUDIO_ANALYSIS_INTERVAL:-5}
|
|
# BRPOP timeout for blocking wait (seconds). Also controls DB reconciliation interval.
|
|
BRPOP_TIMEOUT: ${AUDIO_BRPOP_TIMEOUT:-30}
|
|
# Idle timeout before unloading ML models to free memory (seconds)
|
|
MODEL_IDLE_TIMEOUT: ${AUDIO_MODEL_IDLE_TIMEOUT:-300}
|
|
# Default workers reduced from 8 to 2 for stability (Issue #26 fix)
|
|
# Each worker requires 1-1.5GB RAM normally, up to 2-3GB peak
|
|
NUM_WORKERS: ${AUDIO_ANALYSIS_WORKERS:-2}
|
|
# CPU thread limiting per worker (default 1 for conservative CPU usage)
|
|
THREADS_PER_WORKER: ${AUDIO_ANALYSIS_THREADS_PER_WORKER:-1}
|
|
volumes:
|
|
# Music library (read-only)
|
|
- ${MUSIC_PATH:-./music}:/music:ro
|
|
# NOTE: Models are baked into the Docker image at /app/models
|
|
# No volume mount needed - previously this caused models to be hidden by empty volume
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
# Memory limits prevent OOM kills (Issue #26 fix)
|
|
# 6G limit = safe for 2 workers at 2-3GB peak each
|
|
# 2G reservation = minimum guaranteed memory
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 6G
|
|
cpus: "4"
|
|
reservations:
|
|
memory: 2G
|
|
healthcheck:
|
|
test: ["CMD", "pgrep", "-f", "python.*analyzer"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 60s
|
|
|
|
# CLAP Audio Analyzer - Generates embeddings for similarity search
|
|
audio-analyzer-clap:
|
|
build:
|
|
context: ./services/audio-analyzer-clap
|
|
container_name: kima_audio_analyzer_clap
|
|
environment:
|
|
REDIS_URL: redis://redis:6379
|
|
DATABASE_URL: postgresql://${POSTGRES_USER:-kimadb}:${POSTGRES_PASSWORD:-changeme}@postgres:5432/${POSTGRES_DB:-kima}
|
|
BACKEND_URL: http://backend:3006
|
|
MUSIC_PATH: /music
|
|
SLEEP_INTERVAL: ${CLAP_SLEEP_INTERVAL:-5}
|
|
NUM_WORKERS: ${CLAP_WORKERS:-2}
|
|
THREADS_PER_WORKER: ${CLAP_THREADS_PER_WORKER:-1}
|
|
MODEL_IDLE_TIMEOUT: ${CLAP_MODEL_IDLE_TIMEOUT:-300}
|
|
INTERNAL_API_SECRET: ${INTERNAL_API_SECRET:-kima-internal-secret-change-me}
|
|
volumes:
|
|
- ${MUSIC_PATH:-./music}:/music:ro
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
backend:
|
|
condition: service_healthy
|
|
audio-analyzer:
|
|
condition: service_healthy
|
|
required: false # Allow CLAP to start even if MusicCNN is disabled
|
|
restart: unless-stopped
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
# 3GB is sufficient - we only process 60s chunks
|
|
memory: 3G
|
|
cpus: "4"
|
|
reservations:
|
|
memory: 1G
|
|
|
|
volumes:
|
|
# Kima core
|
|
postgres_data:
|
|
backend_cache:
|
|
backend_logs:
|
|
# External services
|
|
lidarr_config:
|
|
prowlarr_config:
|
|
qbittorrent_config:
|
|
sabnzbd_config:
|
|
|
|
networks:
|
|
default:
|
|
name: kima_network
|