Files
kima-hub/docker-compose.yml
Your Name 60892c12e7 fix: multi-container docker-compose proxy, audiobookshelf sync bugs, add sync button
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
2026-03-18 13:50:18 -05:00

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