Skip to main content

Immich Deployment Notes

This guide summarizes key configuration details for running Immich with GitOps.

Prerequisites

  • Kubernetes cluster with the Zalando Postgres Operator (acid.zalan.do/v1) installed
  • immich namespace created
  • External Secrets Operator available
  • Helm CLI and kubectl configured

Configuration Overview

PostgreSQL Extensions

Ensure the Immich database includes the pgvector and vectorchord extensions:

# k8s/applications/media/immich/database.yaml
spec:
preparedDatabases:
immich:
extensions:
pgvector: public
vectorchord: public

Templated DB_URL

The application expects a single DB_URL. Use ExternalSecrets to assemble the connection string:

# k8s/applications/media/immich/externalsecret.yaml
template:
data:
DB_URL: >-
postgres://immich:{{ .password }}@immich-postgresql:5432/immich?sslmode=require&sslmode=no-verify

External Secrets Permissions

Reference the cluster CA and grant read access to the Zalando secret:

  1. zalando-k8s-store.yaml references kube-root-ca.crt.
  2. serviceaccount.yaml grants get, list, and watch on secrets as well as selfsubjectrulesreviews.

Kustomization Layout

Use a root kustomization.yaml to track all resources:

resources:
- namespace.yaml
- http-route.yaml
- externalsecret.yaml
- database.yaml
- pvc.yaml
- zalando-k8s-store.yaml
- serviceaccount.yaml

OAuth Configuration via ExternalSecret

Immich expects the OAuth client details inside its config file. The immich-config-external-secret.yaml resource pulls these values from Bitwarden and renders the full configuration as a Secret:

# k8s/applications/media/immich/immich-server/immich-config-external-secret.yaml
spec:
template:
data:
immich-config.yaml: |
...
server:
externalDomain: https://photo.pc-tips.se
oauth:
enabled: true
issuerUrl: "https://sso.pc-tips.se/application/o/immich/"
scope: "openid email profile"
autoLaunch: true
autoRegister: true
buttonText: "Login with SSO"
clientId: "{{ .clientId }}"
clientSecret: "{{ .clientSecret }}"
passwordLogin:
enabled: false

This Secret is mounted by the StatefulSet at /config/immich-config.yaml, allowing the application to start without additional environment variables. The machine-learning deployment mounts the same Secret so both components read identical settings.

Resource Requests

Both pods need enough memory to process photos without crashing. A good starting point is:

ComponentCPU RequestMemory RequestCPU LimitMemory Limit
immich-server500m512Mi2000m2Gi
immich-machine-learning200m1Gi1000m4Gi

Library Storage

Immich stores uploaded files on a Persistent Volume Claim named library. The claim requests 40Gi from Longhorn:

# k8s/applications/media/immich/immich-server/statefulset.yaml
spec:
volumeClaimTemplates:
- metadata:
name: library
spec:
storageClassName: longhorn
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 40Gi