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>
This commit is contained in:
2026-01-13 17:23:25 +01:00
parent 3cc9db6632
commit c4fd23855b
4 changed files with 119 additions and 0 deletions

View File

@@ -85,6 +85,13 @@ envs:
MEDIA_JELLYFIN_TRAEFIK_HOST: jellyfin.media.pivoine.art
MEDIA_PINCHFLAT_IMAGE: ghcr.io/kieraneglin/pinchflat:latest
MEDIA_PINCHFLAT_TRAEFIK_HOST: pinchflat.media.pivoine.art
# Immich - Photo and video management
MEDIA_IMMICH_SERVER_IMAGE: ghcr.io/immich-app/immich-server:release
MEDIA_IMMICH_ML_IMAGE: ghcr.io/immich-app/immich-machine-learning:release
MEDIA_IMMICH_POSTGRES_IMAGE: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
MEDIA_IMMICH_TRAEFIK_HOST: immich.media.pivoine.art
MEDIA_IMMICH_DB_NAME: immich
MEDIA_IMMICH_DB_USER: immich
# Dev (Gitea + Coolify)
DEV_TRAEFIK_ENABLED: true
DEV_COMPOSE_PROJECT_NAME: dev

View File

@@ -346,6 +346,25 @@
"yearly": 3
}
}
},
{
"id": "immich-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/immich_postgres_data",
"/volumes/immich_upload"
],
"schedule": {
"cron": "0 12 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
}
]
}

View File

@@ -88,6 +88,8 @@ services:
- backup_dev_gitea_config:/volumes/dev_gitea_config:ro
- backup_dev_gitea_runner_data:/volumes/dev_gitea_runner_data:ro
- backup_dev_coolify_data:/volumes/dev_coolify_data:ro
- backup_media_immich_postgres_data:/volumes/immich_postgres_data:ro
- backup_media_immich_upload:/volumes/immich_upload:ro
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
@@ -200,3 +202,9 @@ volumes:
backup_dev_coolify_data:
name: dev_coolify_data
external: true
backup_media_immich_postgres_data:
name: media_immich_postgres_data
external: true
backup_media_immich_upload:
name: media_immich_upload
external: true

View File

@@ -65,6 +65,85 @@ services:
# 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
@@ -72,6 +151,12 @@ volumes:
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: