apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: nfs-photo namespace: nfs spec: interval: 30m chartRef: kind: OCIRepository name: nfs-photo driftDetection: mode: enabled values: controllers: app: strategy: Recreate podDisruptionBudget: maxUnavailable: "0" initContainers: fix-permissions: image: repository: hectorm/nfs-ganesha tag: v10 command: - sh - -c - chmod -R a+rwX /disks/base /disks/sub1 # native sidecar (restartPolicy: Always): mounts the two disks and # exposes their union at /disks/merged 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" - "allow_other,use_ino,func.create=ff,category.action=all,category.create=ff,category.search=ff" - "/disks/base:/disks/sub1" - "/disks/merged" 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 # runs after mergerfs is confirmed mounted (see dependsOn) and has # /export mounted too, so the /proc/self/mounts snapshot it takes # actually includes the fuse.mergerfs line for /export. ganesha's # FSAL_VFS resolves export paths against this frozen /etc/mtab, not # the live /proc/mounts - if /export is missing from it (e.g. this # ran before mergerfs, or without /export mounted at all), ganesha # fails at startup with "resolve_posix_filesystem(/export) ... # No such file or directory" even though /export is mounted fine. mtab-setup: dependsOn: - mergerfs image: repository: hectorm/nfs-ganesha tag: v10 command: - sh - -c - cat /proc/self/mounts > /mtab-vol/mtab containers: app: image: repository: hectorm/nfs-ganesha tag: v10 securityContext: readOnlyRootFilesystem: true capabilities: drop: - ALL add: - CHOWN - DAC_OVERRIDE - DAC_READ_SEARCH - FOWNER - FSETID - SETGID - SETUID - SYS_RESOURCE probes: liveness: enabled: true spec: initialDelaySeconds: 2 periodSeconds: 10 timeoutSeconds: 2 failureThreshold: 3 readiness: enabled: true spec: initialDelaySeconds: 1 periodSeconds: 2 timeoutSeconds: 1 failureThreshold: 2 resources: requests: cpu: 50m memory: 128Mi limits: memory: 512Mi service: app: controller: app # cluster-internal only, consumed by webdav via # nfs-photo.nfs.svc.cluster.local type: ClusterIP ports: nfs: port: 2049 protocol: TCP persistence: config: type: configMap name: nfs-photo-config globalMounts: - path: /etc/ganesha/ganesha.conf subPath: ganesha.conf readOnly: true base: type: persistentVolumeClaim accessMode: ReadWriteOnce size: 100Gi storageClass: longhorn-single-replica retain: true advancedMounts: app: mergerfs: - path: /disks/base fix-permissions: - path: /disks/base sub1: type: persistentVolumeClaim accessMode: ReadWriteOnce size: 100Gi storageClass: longhorn-single-replica retain: true advancedMounts: app: mergerfs: - path: /disks/sub1 fix-permissions: - path: /disks/sub1 merged: type: emptyDir advancedMounts: app: mergerfs: - path: /disks/merged mountPropagation: Bidirectional app: - path: /export mountPropagation: HostToContainer mtab-setup: - path: /export mountPropagation: HostToContainer dev-fuse: type: hostPath hostPath: /dev/fuse advancedMounts: app: mergerfs: - path: /dev/fuse run: type: emptyDir globalMounts: - path: /run tmp: type: emptyDir globalMounts: - path: /tmp recovery: type: emptyDir globalMounts: - path: /recovery etc-mtab: type: emptyDir advancedMounts: app: app: - path: /etc/mtab subPath: mtab mtab-setup: - path: /mtab-vol