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 podDisruptionBudget: maxUnavailable: "0" initContainers: fix-permissions: image: repository: hectorm/nfs-ganesha tag: v10 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 # everything immich keeps outside "library" (backups, encoded-video, # profile, thumbs, upload) - plain volume, no mergerfs involved. other: type: persistentVolumeClaim accessMode: ReadWriteOnce size: 200Gi storageClass: longhorn-single-replica retain: true advancedMounts: server: app: - path: /data