Last active
September 15, 2026 08:43
-
-
Save vdboor/4a596a4541e98318801461556a295ee7 to your computer and use it in GitHub Desktop.
Immich installation on K3S / Kubenetes with: background workers - CloudNativePG - external storage.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # https://github.com/immich-app/immich-charts/blob/main/charts/immich/values.yaml | |
| # helm install --create-namespace --namespace immich immich oci://ghcr.io/immich-app/immich-charts/immich -f values.yaml | |
| # helm upgrade --namespace immich immich oci://ghcr.io/immich-app/immich-charts/immich -f values.yml | |
| controllers: | |
| main: | |
| containers: | |
| main: | |
| image: | |
| tag: v3.2.1 | |
| server: | |
| controllers: | |
| # Main worker serving web/api requests: | |
| main: | |
| containers: | |
| main: | |
| env: | |
| IMMICH_WORKERS_INCLUDE: 'api' # Only serve HTTP requests | |
| TZ: "Europe/Amsterdam" | |
| REDIS_HOSTNAME: 'redis-master.infra' # Simple shared redis pod, nothing special | |
| REDIS_PASSWORD: '<snip>' | |
| REDIS_DBINDEX: 13 | |
| DB_HOSTNAME: 'immich-postgres-rw' # Deployed by CloudNativePG | |
| DB_DATABASE_NAME: 'immich' | |
| DB_USERNAME: | |
| valueFrom: | |
| secretKeyRef: | |
| name: immich-db-user | |
| key: username | |
| DB_PASSWORD: | |
| valueFrom: | |
| secretKeyRef: | |
| name: immich-db-user | |
| key: password | |
| # Background worker in a separate pod. | |
| # This avoids a hanging main api process during background processing. | |
| microservices: | |
| containers: | |
| microservices: | |
| image: | |
| repository: ghcr.io/immich-app/immich-server | |
| tag: v3.2.1 | |
| env: | |
| IMMICH_WORKERS_EXCLUDE: 'api' # Background workers, no HTTP interface. | |
| # Rest need to be the same as api container: | |
| TZ: "Europe/Amsterdam" | |
| REDIS_HOSTNAME: 'redis-master.infra' | |
| REDIS_PASSWORD: '<snip>' | |
| REDIS_DBINDEX: 13 | |
| DB_HOSTNAME: 'immich-postgres-rw' | |
| DB_DATABASE_NAME: 'immich' | |
| DB_USERNAME: | |
| valueFrom: | |
| secretKeyRef: | |
| name: immich-db-user | |
| key: username | |
| DB_PASSWORD: | |
| valueFrom: | |
| secretKeyRef: | |
| name: immich-db-user | |
| key: password | |
| # Webserver configuration | |
| ingress: | |
| main: | |
| enabled: true | |
| className: nginx | |
| hosts: | |
| - host: photos.example.com | |
| paths: | |
| - path: "/" | |
| service: | |
| identifier: main | |
| - host: immich | |
| paths: | |
| - path: "/" | |
| service: | |
| identifier: main | |
| - host: immich.home | |
| paths: | |
| - path: "/" | |
| service: | |
| identifier: main | |
| valkey: | |
| enabled: true | |
| #persistence: | |
| # data: | |
| # size: 100Mi | |
| # type: persistentVolumeClaim | |
| persistence: | |
| # Defining persistence in other controlers didn't work | |
| # Placing here globally, it will be added to all pods however | |
| external: | |
| enabled: true | |
| type: hostPath | |
| hostPath: /mnt/host-fotos | |
| globalMounts: | |
| - path: /external/ | |
| readOnly: true | |
| # Persistent volumes are defined below, not using the yaml options here. | |
| # This allowed using extra options for the PVC objects that helm doesn't expose. | |
| immich: | |
| configurationKind: Secret | |
| persistence: | |
| library: | |
| existingClaim: "immich-library" | |
| microservices: | |
| persistence: | |
| library: | |
| existingClaim: "immich-library" | |
| machine-learning: | |
| persistence: | |
| cache: | |
| type: persistentVolumeClaim | |
| existingClaim: "immich-machine-learning" | |
| #accessMode: ReadWriteOnce | |
| #size: 2Gi # not 10 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # CloudNativePG database on the cluster | |
| # This performs the "CREATE DATABASE" and "CREATE EXTENSION" on the cluster. | |
| apiVersion: postgresql.cnpg.io/v1 | |
| kind: Database | |
| metadata: | |
| name: immich-database | |
| namespace: immich | |
| spec: | |
| name: immich | |
| owner: immich | |
| cluster: | |
| name: immich-postgres # Cluster created below | |
| extensions: | |
| - name: vector | |
| ensure: present | |
| - name: vchord | |
| ensure: present | |
| - name: cube | |
| ensure: present | |
| - name: earthdistance | |
| ensure: present |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # PostgreSQL user for CloudNativePG | |
| # Used in various other yaml files. | |
| apiVersion: v1 | |
| kind: Secret | |
| type: kubernetes.io/basic-auth | |
| metadata: | |
| name: immich-db-user | |
| namespace: immich | |
| labels: | |
| cnpg.io/reload: "true" | |
| data: | |
| username: <base64-encoded-snip> | |
| password: <base64-encoded-snip> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # CloudNativePG Postgres cluster. | |
| # This spawns the whole PostgreSQL database container. | |
| # It uses a custom image, as mounting the vchord.so via another image didn't work. | |
| apiVersion: postgresql.cnpg.io/v1 | |
| kind: Cluster | |
| metadata: | |
| name: immich-postgres | |
| namespace: immich | |
| spec: | |
| instances: 1 | |
| # Custom image here, that has the vchord.so included. | |
| # default: ghcr.io/cloudnative-pg/postgresql:18-standard-trixie | |
| imageName: MYREGISTRY.example.com/cnpg-postgresql:18-trixie-vchord | |
| storage: | |
| size: 5Gi | |
| storageClass: local-storage | |
| resizeInUseVolumes: False | |
| pvcTemplate: | |
| dataSource: | |
| apiGroup: v1 | |
| kind: PersistentVolumeClaim | |
| name: immich-postgres-0-pvc | |
| postgresql: | |
| # Make sure the extension .so file is available at the server. | |
| # The Database CRD will activate it for the database (CREATE EXTENSION). | |
| shared_preload_libraries: | |
| - "vchord.so" | |
| # This didn't work for me: | |
| #extensions: | |
| # - name: vchord | |
| # image: | |
| # reference: ghcr.io/tensorchord/vchord-scratch:pg18-v1.1.1 | |
| # dynamic_library_path: | |
| # - /usr/lib/postgresql/18/lib | |
| # extension_control_path: | |
| # - /usr/share/postgresql/18/ | |
| managed: | |
| roles: | |
| - name: immich | |
| comment: Custom application user | |
| login: true | |
| passwordSecret: | |
| name: immich-db-user |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Persistent volumes and claims | |
| # | |
| # For various reasons, I'm using the storage from the host OS here | |
| # (which is mounted as virtiofs passthrough): | |
| # - The database files of various posts are stored on /mnt/immich/ | |
| # - The external photo library is found on /mnt/host-photos. | |
| # It avoiding needing network access to the external photo library. | |
| # Hence, the volumes are forced to the K3S node that has this mount point. | |
| # | |
| # The volume claims are included here too, to make sure the PV and PVC match. | |
| # This way 'values.yml' only needs to link to existing claims. | |
| # | |
| # In larger clusters, you'd probably use the PVC alone | |
| # (or even let values.yaml generate the claims resources), | |
| # and let Kubernetes allocate the storage on a network share. | |
| # | |
| kind: PersistentVolume | |
| apiVersion: v1 | |
| metadata: | |
| name: immich-library | |
| namespace: immich | |
| labels: | |
| component: immich-library | |
| spec: | |
| storageClassName: local-storage | |
| persistentVolumeReclaimPolicy: Retain | |
| capacity: | |
| storage: 30Gi | |
| accessModes: | |
| - ReadWriteOnce | |
| local: | |
| path: "/mnt/immich/library" | |
| nodeAffinity: | |
| required: | |
| nodeSelectorTerms: | |
| - matchExpressions: | |
| - key: kubernetes.io/hostname | |
| operator: In | |
| values: | |
| - <snip-MY_K3S_IMMICH_NODE_NAME> | |
| --- | |
| apiVersion: v1 | |
| kind: PersistentVolumeClaim | |
| metadata: | |
| name: immich-library | |
| namespace: immich | |
| spec: | |
| storageClassName: local-storage | |
| selector: | |
| matchLabels: | |
| component: immich-library | |
| accessModes: | |
| - ReadWriteOnce | |
| resources: | |
| requests: | |
| storage: 30Gi | |
| --- | |
| kind: PersistentVolume | |
| apiVersion: v1 | |
| metadata: | |
| name: immich-machine-learning | |
| namespace: immich | |
| labels: | |
| component: immich-machine-learning | |
| spec: | |
| storageClassName: local-storage | |
| persistentVolumeReclaimPolicy: Retain | |
| capacity: | |
| storage: 10Gi | |
| accessModes: | |
| - ReadWriteOnce | |
| local: | |
| path: "/mnt/immich/machine-learning" | |
| nodeAffinity: | |
| required: | |
| nodeSelectorTerms: | |
| - matchExpressions: | |
| - key: kubernetes.io/hostname | |
| operator: In | |
| values: | |
| - <snip-MY_K3S_IMMICH_NODE_NAME> | |
| --- | |
| apiVersion: v1 | |
| kind: PersistentVolumeClaim | |
| metadata: | |
| name: immich-machine-learning | |
| namespace: immich | |
| spec: | |
| storageClassName: local-storage | |
| selector: | |
| matchLabels: | |
| component: immich-machine-learning | |
| accessModes: | |
| - ReadWriteOnce | |
| resources: | |
| requests: | |
| storage: 10Gi | |
| --- | |
| kind: PersistentVolume | |
| apiVersion: v1 | |
| metadata: | |
| name: immich-postgres-0 | |
| namespace: immich | |
| labels: | |
| component: immich-postgres | |
| spec: | |
| storageClassName: local-storage | |
| persistentVolumeReclaimPolicy: Retain | |
| capacity: | |
| storage: 5Gi | |
| accessModes: | |
| - ReadWriteOnce | |
| local: | |
| path: "/mnt/immich/postgres-0" | |
| nodeAffinity: | |
| required: | |
| nodeSelectorTerms: | |
| - matchExpressions: | |
| - key: kubernetes.io/hostname | |
| operator: In | |
| values: | |
| - <snip-MY_K3S_IMMICH_NODE_NAME> | |
| --- | |
| apiVersion: v1 | |
| kind: PersistentVolumeClaim | |
| metadata: | |
| name: immich-postgres-0 | |
| namespace: immich | |
| spec: | |
| storageClassName: local-storage | |
| selector: | |
| matchLabels: | |
| component: immich-postgres | |
| accessModes: | |
| - ReadWriteOnce | |
| resources: | |
| requests: | |
| storage: 5Gi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Local storage provider settings. | |
| # This will provision storage volumes on the node where the pod starts. | |
| # | |
| apiVersion: storage.k8s.io/v1 | |
| kind: StorageClass | |
| metadata: | |
| name: local-storage | |
| provisioner: kubernetes.io/no-provisioner | |
| volumeBindingMode: WaitForFirstConsumer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Building the custom CloudNativePG image that has the vchord extension. | |
| # | |
| # docker build --progress=plain --pull -t MYREGISTRY.example.com/cnpg-postgresql:18-trixie-vchord . | |
| # docker push MYREGISTRY.example.com/cnpg-postgresql:18-trixie-vchord | |
| FROM ghcr.io/tensorchord/vchord-scratch:pg18-v1.1.1 AS ext | |
| FROM ghcr.io/cloudnative-pg/postgresql:18-standard-trixie | |
| COPY --from=ext ./usr/lib/postgresql/18/lib/vchord.so /usr/lib/postgresql/18/lib/ | |
| COPY --from=ext /usr/share/postgresql/18/extension/vchord* /usr/share/postgresql/18/extension/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment