immich mergerfs

This commit is contained in:
azykov@mail.ru
2026-08-22 09:55:19 +03:00
parent 87cc20df72
commit 25e74fc0cb
13 changed files with 339 additions and 134 deletions
+127 -10
View File
@@ -13,6 +13,66 @@ spec:
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. Same
# pattern as clusters/base/apps/nfs-photo.
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:
@@ -54,10 +114,12 @@ spec:
limits:
memory: 4Gi
# one-shot migration copy from the old NAS-hosted docker install
# into this install's own library volume (see "library" below).
# into this install's own volumes (see "persistence" below).
# rsync is idempotent, so a pod restart mid-copy just resumes
# cleanly; once done it sleeps forever instead of exiting, so the
# container doesn't restart-loop and the status page stays up.
# (starts after all initContainers - fix-permissions and the
# mergerfs sidecar's startupProbe - complete, same as "app")
rsync:
image:
repository: python
@@ -68,10 +130,10 @@ spec:
- apk add --no-cache rsync >/dev/null && python3 /app/rsync.py
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
rsync-web:
image:
repository: nginx
@@ -141,16 +203,71 @@ spec:
- rsync.photo.aggtaa.com
persistence:
# brand new, dedicated library - deliberately not reusing the old
# docker-compose install's NAS data, and not nfs-photo either.
library:
# 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: 800Gi
size: 200Gi
storageClass: longhorn-single-replica
retain: true
globalMounts:
- path: /data
advancedMounts:
server:
mergerfs:
- path: /disks/library-recent
fix-permissions:
- path: /disks/library-recent
rsync:
- 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
rsync:
- 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: 100Gi
storageClass: longhorn-single-replica
retain: true
advancedMounts:
server:
app:
- path: /data
rsync:
- path: /data
nas-source:
type: persistentVolumeClaim
existingClaim: immich-nas-source