254 lines
8.4 KiB
YAML
254 lines
8.4 KiB
YAML
apiVersion: helm.toolkit.fluxcd.io/v2
|
|
kind: HelmRelease
|
|
metadata:
|
|
name: immich-server
|
|
namespace: photo
|
|
spec:
|
|
interval: 30m
|
|
chartRef:
|
|
kind: OCIRepository
|
|
name: immich
|
|
driftDetection:
|
|
mode: enabled
|
|
values:
|
|
controllers:
|
|
server:
|
|
strategy: Recreate
|
|
# temporarily disabled while /data is being split into separate
|
|
# per-purpose Longhorn volumes (encoded-video, thumbs, backups,
|
|
# data) and the old combined "other" volume is preserved untouched
|
|
# as a fallback, remounted at /data-old.
|
|
replicas: 0
|
|
podDisruptionBudget:
|
|
maxUnavailable: "0"
|
|
initContainers:
|
|
fix-permissions:
|
|
image:
|
|
repository: hectorm/nfs-ganesha
|
|
tag: v10
|
|
# hectorm does not publish exact versions, v10.0 digest is 93df70b78...
|
|
digest: sha256:93df70b78aae56957f4a4f5c606c4250d84b729435f76132dca07b7290f5bfce
|
|
command:
|
|
- sh
|
|
- -c
|
|
- chmod -R a+rwX /disks/library-recent /disks/library-archive
|
|
# native sidecar (restartPolicy: Always): mounts the two library
|
|
# disks and exposes their union at /disks/merged-library via
|
|
# FUSE. It must be up and actually mounted *before* the "app"
|
|
# container is created, since HostToContainer propagation only
|
|
# relays mounts made *after* the receiving container's own mount
|
|
# exists - it won't retroactively pick up a FUSE mount that's
|
|
# already there. Running it as a restartPolicy:Always init
|
|
# container (started, gated on the startupProbe below, before
|
|
# regular containers) guarantees that ordering; a regular sidecar
|
|
# container racing "app" on every pod start does not.
|
|
mergerfs:
|
|
dependsOn:
|
|
- fix-permissions
|
|
restartPolicy: Always
|
|
image:
|
|
repository: ghcr.io/hotio/mergerfs
|
|
tag: release-v2.42.0
|
|
args:
|
|
- "-o"
|
|
# library-recent listed first: category.create=ff means new
|
|
# files created through the merged mount (i.e. new immich
|
|
# uploads going forward) land on library-recent by default,
|
|
# which is exactly what's wanted for freshly-uploaded photos.
|
|
- "allow_other,use_ino,func.create=ff,category.action=all,category.create=ff,category.search=newest"
|
|
- "/disks/library-recent:/disks/library-archive"
|
|
- "/disks/merged-library"
|
|
securityContext:
|
|
privileged: true
|
|
probes:
|
|
startup:
|
|
enabled: true
|
|
custom: true
|
|
spec:
|
|
exec:
|
|
command:
|
|
- sh
|
|
- -c
|
|
- grep -q fuse.mergerfs /proc/mounts
|
|
periodSeconds: 1
|
|
timeoutSeconds: 2
|
|
failureThreshold: 30
|
|
resources:
|
|
requests:
|
|
cpu: 50m
|
|
memory: 64Mi
|
|
limits:
|
|
memory: 512Mi
|
|
containers:
|
|
app:
|
|
image:
|
|
repository: ghcr.io/immich-app/immich-server
|
|
# make sure immich-machine-learning version match this
|
|
tag: v3.1.0
|
|
env:
|
|
TZ: Europe/Moscow
|
|
DB_HOSTNAME: immich-postgres
|
|
DB_PORT: "5432"
|
|
DB_USERNAME: postgres
|
|
DB_DATABASE_NAME: immich
|
|
DB_PASSWORD:
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: immich-secrets
|
|
key: DB_PASSWORD
|
|
REDIS_HOSTNAME: immich-redis
|
|
REDIS_PORT: "6379"
|
|
IMMICH_MACHINE_LEARNING_URL: http://immich-ml:3003
|
|
probes:
|
|
# first boot runs DB migrations + vector index setup, which
|
|
# can take a while - without a startup probe the default
|
|
# liveness probe (periodSeconds=10, failureThreshold=3) kills
|
|
# the container ~30s in, well before it's finished binding
|
|
# the port, causing an endless restart loop.
|
|
startup:
|
|
enabled: true
|
|
spec:
|
|
periodSeconds: 5
|
|
failureThreshold: 60
|
|
liveness:
|
|
enabled: true
|
|
readiness:
|
|
enabled: true
|
|
resources:
|
|
requests:
|
|
cpu: 200m
|
|
memory: 512Mi
|
|
limits:
|
|
memory: 4Gi
|
|
|
|
service:
|
|
app:
|
|
controller: server
|
|
ports:
|
|
http:
|
|
port: 2283
|
|
|
|
ingress:
|
|
app:
|
|
className: traefik
|
|
annotations:
|
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
hosts:
|
|
- host: photo.aggtaa.com
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
service:
|
|
identifier: app
|
|
port: http
|
|
tls:
|
|
- secretName: immich-tls
|
|
hosts:
|
|
- photo.aggtaa.com
|
|
|
|
persistence:
|
|
# Immich's "library" folder, mergerfs-merged from two disks: recent
|
|
# files (per the rsync migration's cutoff) and everything else.
|
|
# New uploads going forward land on library-recent (see mergerfs
|
|
# category.create above). Not reusing the old single 800Gi library
|
|
# PVC/PV - this replaces it (that PVC becomes orphaned, retained,
|
|
# and unused; clean it up separately if no longer wanted).
|
|
library-recent:
|
|
type: persistentVolumeClaim
|
|
accessMode: ReadWriteOnce
|
|
size: 200Gi
|
|
storageClass: longhorn-single-replica
|
|
retain: true
|
|
advancedMounts:
|
|
server:
|
|
mergerfs:
|
|
- path: /disks/library-recent
|
|
fix-permissions:
|
|
- path: /disks/library-recent
|
|
library-archive:
|
|
type: persistentVolumeClaim
|
|
accessMode: ReadWriteOnce
|
|
size: 500Gi
|
|
storageClass: longhorn-single-replica
|
|
retain: true
|
|
advancedMounts:
|
|
server:
|
|
mergerfs:
|
|
- path: /disks/library-archive
|
|
fix-permissions:
|
|
- path: /disks/library-archive
|
|
merged-library:
|
|
type: emptyDir
|
|
advancedMounts:
|
|
server:
|
|
mergerfs:
|
|
- path: /disks/merged-library
|
|
mountPropagation: Bidirectional
|
|
app:
|
|
- path: /data/library
|
|
mountPropagation: HostToContainer
|
|
dev-fuse:
|
|
type: hostPath
|
|
hostPath: /dev/fuse
|
|
advancedMounts:
|
|
server:
|
|
mergerfs:
|
|
- path: /dev/fuse
|
|
# split out of the old combined "other" volume below: each of these
|
|
# purposes now gets its own Longhorn volume instead of sharing one
|
|
# 200Gi disk, so a single hot subtree (e.g. thumbs) doesn't force
|
|
# replica rebuilds/relocation to drag the whole 200Gi with it.
|
|
data:
|
|
type: persistentVolumeClaim
|
|
accessMode: ReadWriteOnce
|
|
size: 20Gi
|
|
storageClass: longhorn
|
|
retain: true
|
|
advancedMounts:
|
|
server:
|
|
app:
|
|
- path: /data
|
|
encoded-video:
|
|
type: persistentVolumeClaim
|
|
accessMode: ReadWriteOnce
|
|
size: 100Gi
|
|
storageClass: longhorn
|
|
retain: true
|
|
advancedMounts:
|
|
server:
|
|
app:
|
|
- path: /data/encoded-video
|
|
thumbs:
|
|
type: persistentVolumeClaim
|
|
accessMode: ReadWriteOnce
|
|
size: 100Gi
|
|
storageClass: longhorn
|
|
retain: true
|
|
advancedMounts:
|
|
server:
|
|
app:
|
|
- path: /data/thumbs
|
|
backups:
|
|
type: persistentVolumeClaim
|
|
accessMode: ReadWriteOnce
|
|
size: 30Gi
|
|
storageClass: longhorn
|
|
retain: true
|
|
advancedMounts:
|
|
server:
|
|
app:
|
|
- path: /data/backups
|
|
# old combined volume (backups, encoded-video, profile, thumbs,
|
|
# upload) - kept mounted read-write as a fallback/reference copy at
|
|
# /data-old, untouched, until the split above is confirmed good.
|
|
other:
|
|
type: persistentVolumeClaim
|
|
accessMode: ReadWriteOnce
|
|
size: 200Gi
|
|
storageClass: longhorn-single-replica
|
|
retain: true
|
|
advancedMounts:
|
|
server:
|
|
app:
|
|
- path: /data-old
|