Files
docker-compose/media/compose.yaml
Sebastian Krüger c4fd23855b feat(media): add Immich photo/video management service
- Add immich_server, immich_ml, and immich_postgres services
- Use dedicated PostgreSQL with vector extensions (vectorchord + pgvectors)
- Connect to core Redis for job queues
- Configure Traefik routing for immich.media.pivoine.art
- Add backup volumes and plan for Backrest (daily at 12:00)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 17:23:25 +01:00

165 lines
8.1 KiB
YAML

services:
# Jellyfin - Media streaming server
jellyfin:
image: ${MEDIA_JELLYFIN_IMAGE:-jellyfin/jellyfin:latest}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin
restart: unless-stopped
volumes:
- jellyfin_config:/config
- jellyfin_cache:/cache
- /mnt/hidrive/users/valknar/Pictures:/media/pictures:ro
- /mnt/hidrive/users/valknar/Videos:/media/videos:ro
- /mnt/hidrive/users/valknar/Music:/media/music:ro
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
networks:
- compose_network
labels:
- 'traefik.enable=${MEDIA_TRAEFIK_ENABLED}'
# HTTP to HTTPS redirect
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-redirect-web-secure.redirectscheme.scheme=https'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-redirect-web-secure'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web.rule=Host(`${MEDIA_JELLYFIN_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web.entrypoints=web'
# HTTPS router
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web-secure.rule=Host(`${MEDIA_JELLYFIN_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web-secure.tls.certresolver=resolver'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web-secure.entrypoints=web-secure'
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web-secure-compress.compress=true'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web-secure.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web-secure-compress,security-headers@file'
# Service
- 'traefik.http.services.${MEDIA_COMPOSE_PROJECT_NAME}-jellyfin-web-secure.loadbalancer.server.port=8096'
- 'traefik.docker.network=${NETWORK_NAME}'
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Pinchflat - YouTube download manager
pinchflat:
image: ${MEDIA_PINCHFLAT_IMAGE:-ghcr.io/kieraneglin/pinchflat:latest}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_pinchflat
restart: unless-stopped
volumes:
- pinchflat_config:/config
- /mnt/hidrive/users/valknar/Downloads/pinchflat:/downloads
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
JOURNAL_MODE: delete
networks:
- compose_network
labels:
- 'traefik.enable=${MEDIA_TRAEFIK_ENABLED}'
# HTTP to HTTPS redirect
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-redirect-web-secure.redirectscheme.scheme=https'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-redirect-web-secure'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web.rule=Host(`${MEDIA_PINCHFLAT_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web.entrypoints=web'
# HTTPS router with Authelia SSO protection
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web-secure.rule=Host(`${MEDIA_PINCHFLAT_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web-secure.tls.certresolver=resolver'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web-secure.entrypoints=web-secure'
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web-secure-compress.compress=true'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web-secure.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web-secure-compress,net-authelia,security-headers@file'
# Service
- 'traefik.http.services.${MEDIA_COMPOSE_PROJECT_NAME}-pinchflat-web-secure.loadbalancer.server.port=8945'
- 'traefik.docker.network=${NETWORK_NAME}'
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Immich PostgreSQL - Dedicated database with vector extensions
immich_postgres:
image: ${MEDIA_IMMICH_POSTGRES_IMAGE:-ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_postgres
restart: unless-stopped
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
POSTGRES_USER: ${MEDIA_IMMICH_DB_USER:-immich}
POSTGRES_PASSWORD: ${MEDIA_IMMICH_DB_PASSWORD}
POSTGRES_DB: ${MEDIA_IMMICH_DB_NAME:-immich}
POSTGRES_INITDB_ARGS: --data-checksums
volumes:
- immich_postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${MEDIA_IMMICH_DB_USER:-immich} -d ${MEDIA_IMMICH_DB_NAME:-immich}"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- compose_network
# Immich Server - Main application
immich_server:
image: ${MEDIA_IMMICH_SERVER_IMAGE:-ghcr.io/immich-app/immich-server:release}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_server
restart: unless-stopped
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
DB_HOSTNAME: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_postgres
DB_PORT: 5432
DB_USERNAME: ${MEDIA_IMMICH_DB_USER:-immich}
DB_PASSWORD: ${MEDIA_IMMICH_DB_PASSWORD}
DB_DATABASE_NAME: ${MEDIA_IMMICH_DB_NAME:-immich}
REDIS_HOSTNAME: ${CORE_COMPOSE_PROJECT_NAME}_redis
REDIS_PORT: ${CORE_REDIS_PORT:-6379}
IMMICH_MACHINE_LEARNING_URL: http://${MEDIA_COMPOSE_PROJECT_NAME}_immich_ml:3003
volumes:
- immich_upload:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
depends_on:
immich_postgres:
condition: service_healthy
networks:
- compose_network
labels:
- 'traefik.enable=${MEDIA_TRAEFIK_ENABLED}'
# HTTP to HTTPS redirect
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-immich-redirect-web-secure.redirectscheme.scheme=https'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-immich-redirect-web-secure'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web.rule=Host(`${MEDIA_IMMICH_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web.entrypoints=web'
# HTTPS router
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.rule=Host(`${MEDIA_IMMICH_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.tls.certresolver=resolver'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.entrypoints=web-secure'
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure-compress.compress=true'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure-compress,security-headers@file'
# Service
- 'traefik.http.services.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.loadbalancer.server.port=2283'
- 'traefik.docker.network=${NETWORK_NAME}'
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Immich Machine Learning - AI inference for faces, search, etc.
immich_ml:
image: ${MEDIA_IMMICH_ML_IMAGE:-ghcr.io/immich-app/immich-machine-learning:release}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_ml
restart: unless-stopped
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
volumes:
- immich_model_cache:/cache
networks:
- compose_network
labels:
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
volumes:
jellyfin_config:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin_config
jellyfin_cache:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin_cache
pinchflat_config:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_pinchflat_config
immich_postgres_data:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_postgres_data
immich_upload:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_upload
immich_model_cache:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_model_cache
networks:
compose_network:
name: ${NETWORK_NAME}
external: true