immich added

This commit is contained in:
2025-10-27 09:44:37 +01:00
commit e6f3536e9e
37 changed files with 2670 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
source "$(git rev-parse --show-toplevel)/.envrc"
export KUBECTL_NAMESPACE="$(grep "name:" namespace.yaml | awk '{print $2}')"
kubectl config set-context --current --namespace=$KUBECTL_NAMESPACE

View File

@@ -0,0 +1,28 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageUpdateAutomation
metadata:
name: gitea
namespace: immich
spec:
interval: 30m
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
git:
checkout:
ref:
branch: main
commit:
author:
email: fluxcdbot@gitea.example.cloud
name: fluxcdbot
messageTemplate:
'{{range .Changed.Changes}}{{print .OldValue}} -> {{println
.NewValue}}{{end}}'
push:
branch: main
update:
path: ./cluster/apps/immich/
strategy: Setters

View File

@@ -0,0 +1,14 @@
---
apiVersion: v1
kind: LimitRange
metadata:
name: immich
namespace: immich
spec:
limits:
- default:
memory: 100Mi
defaultRequest:
cpu: 10m
memory: 100Mi
type: Container

View File

@@ -0,0 +1,13 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: immich
namespace: immich
labels:
app: immich
data:
# https://immich.app/docs/install/environment-variables/
TZ: 'Europe/Berlin'
REDIS_HOSTNAME: 'immich-valkey.immich.svc.cluster.local'
IMMICH_TELEMETRY_INCLUDE: 'all'

View File

@@ -0,0 +1,50 @@
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: immich-db
namespace: immich
labels:
app: immich-db
velero.io/exclude-from-backup: 'true'
spec:
# https://github.com/tensorchord/cloudnative-vectorchord/pkgs/container/cloudnative-vectorchord
imageName: ghcr.io/tensorchord/cloudnative-vectorchord:16-0.4.3
instances: 2
resources:
limits:
memory: 4Gi
requests:
memory: 2Gi
cpu: 20m
# longhorn: because of easier size restriction
storage:
size: 4Gi
storageClass: longhorn
managed:
roles:
- name: app
# we make it as superuser otherwise it can happen that immich fails to start because if is checking / creating the extensions in postgres db
# it can be done manually but this makes life much easier
superuser: true
login: true
postgresql:
parameters:
shared_buffers: 2GB
archive_timeout: 60min
shared_preload_libraries:
- 'vchord.so'
enableAlterSystem: true
plugins:
- name: barman-cloud.cloudnative-pg.io
isWALArchiver: true
parameters:
barmanObjectName: immich-db
monitoring:
enablePodMonitor: true

View File

View File

@@ -0,0 +1,17 @@
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: immich-db-daily
namespace: immich
labels:
app: immich-db
spec:
cluster:
name: immich-db
schedule: '0 0 2 * * *'
backupOwnerReference: self
immediate: true
method: plugin
pluginConfiguration:
name: barman-cloud.cloudnative-pg.io

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImagePolicy
metadata:
name: immich-exporter
namespace: immich
spec:
imageRepositoryRef:
name: immich-exporter
policy:
semver:
range: '1.*.*'

View File

@@ -0,0 +1,12 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
name: immich-exporter
namespace: immich
spec:
# https://hub.docker.com/r/friendlyfriend/prometheus-immich-exporter/tags
image: friendlyfriend/prometheus-immich-exporter
interval: 24h
exclusionList:
- latest

View File

@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: Service
metadata:
name: immich-exporter
namespace: immich
labels:
app: immich-exporter
spec:
ports:
- name: http
port: 80
targetPort: http
selector:
app: immich-exporter

View File

@@ -0,0 +1,18 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: immich-exporter
namespace: immich
labels:
app: immich-exporter
spec:
selector:
matchLabels:
app: immich-exporter
endpoints:
- port: http # port name of service
scheme: http
interval: 15s
metricRelabelings:
- action: labeldrop
regex: (instance|pod)

View File

@@ -0,0 +1,12 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: immich-exporter
namespace: immich
labels:
app: immich-exporter
data:
# https://immich.app/docs/install/environment-variables/
IMMICH_HOST: 'immich-server.immich.svc.cluster.local'
IMMICH_PORT: '80'

View File

@@ -0,0 +1,65 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: immich-exporter
namespace: immich
annotations:
reloader.stakater.com/auto: 'true'
labels:
app: immich-exporter
spec:
replicas: 1
selector:
matchLabels:
app: immich-exporter
template:
metadata:
labels:
app: immich-exporter
spec:
automountServiceAccountToken: false
containers:
- name: immich-exporter
# https://github.com/friendlyFriend4000/prometheus-immich-exporter
image: friendlyfriend/prometheus-immich-exporter:1.2.3 # {"$imagepolicy": "immich:immich-exporter"}
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 100Mi
requests:
memory: 25Mi
cpu: 1m
ports:
- name: http
containerPort: 8000
protocol: TCP
env:
- name: TZ
value: 'Europe/Berlin'
envFrom:
- configMapRef:
name: immich-exporter
optional: false
- secretRef:
name: immich-exporter
optional: false
securityContext:
privileged: false
seccompProfile:
type: 'RuntimeDefault'
livenessProbe:
httpGet:
path: /metrics
port: http
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /metrics
port: http
initialDelaySeconds: 5
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3

View File

@@ -0,0 +1,10 @@
---
apiVersion: v1
kind: Secret
metadata:
name: immich-exporter
namespace: immich
labels:
app: immich-exporter
stringData:
IMMICH_API_TOKEN: change_me

View File

@@ -0,0 +1,84 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: immich-machine-learning
namespace: immich
annotations:
reloader.stakater.com/auto: 'true'
labels:
app: immich-machine-learning
spec:
selector:
matchLabels:
app: immich-machine-learning
template:
metadata:
labels:
app: immich-machine-learning
spec:
automountServiceAccountToken: false
containers:
- name: immich-machine-learning
image: ghcr.io/immich-app/immich-machine-learning:v2.1.0 # {"$imagepolicy": "immich:immich-machine-learning"}
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 8Gi
requests:
memory: 500Mi
cpu: 500m
env:
- name: DB_HOSTNAME
valueFrom:
secretKeyRef:
name: immich-db-app
key: host
optional: false
- name: DB_DATABASE_NAME
valueFrom:
secretKeyRef:
name: immich-db-app
key: dbname
optional: false
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: immich-db-app
key: username
optional: false
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: immich-db-app
key: password
optional: false
envFrom:
- configMapRef:
name: immich
optional: false
livenessProbe:
httpGet:
path: /ping
port: 3003
initialDelaySeconds: 60
readinessProbe:
httpGet:
path: /ping
port: 3003
startupProbe:
httpGet:
path: /ping
port: 3003
securityContext:
privileged: true # for intel quick sync
seccompProfile:
type: 'RuntimeDefault'
volumeMounts:
- name: immich-machine-learning
mountPath: /cache
nodeSelector:
intel.feature.node.kubernetes.io/gpu: 'true'
volumes:
- name: immich-machine-learning
persistentVolumeClaim:
claimName: immich-machine-learning

View File

@@ -0,0 +1,15 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImagePolicy
metadata:
name: immich-machine-learning
namespace: immich
spec:
imageRepositoryRef:
name: immich-machine-learning
policy:
# https://github.com/immich-app/immich/releases
semver:
range: '2.*.*'
filterTags:
pattern: 'v.*'

View File

@@ -0,0 +1,17 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
name: immich-machine-learning
namespace: immich
spec:
# https://github.com/immich-app/immich/pkgs/container/immich-machine-learning
image: ghcr.io/immich-app/immich-machine-learning
interval: 24h
exclusionList:
- .*openvino
- .*cuda
- .*armnn
- pr*
- main
- main*

View File

@@ -0,0 +1,16 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: immich-machine-learning
namespace: immich
labels:
app: immich-machine-learning
velero.io/exclude-from-backup: 'true' # because this is nfs storage
spec:
accessModes:
- ReadWriteMany
storageClassName: k8s-nfs-ssd
resources:
requests:
storage: 10Gi

View File

@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: Service
metadata:
name: immich-machine-learning
namespace: immich
labels:
app: immich-machine-learning
spec:
ports:
- name: http
targetPort: 3003
port: 3003
selector:
app: immich-machine-learning

View File

@@ -0,0 +1,12 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: immich
labels:
# https://kubernetes.io/docs/concepts/security/pod-security-standards/
# possible values: privileged, baseline, restricted
# set privileged because of intel quick sync
pod-security.kubernetes.io/enforce: privileged
pod-security.kubernetes.io/audit: privileged
pod-security.kubernetes.io/warn: privileged

View File

@@ -0,0 +1,15 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImagePolicy
metadata:
name: immich-server
namespace: immich
spec:
imageRepositoryRef:
name: immich-server
policy:
# https://github.com/immich-app/immich/releases
semver:
range: '2.*.*'
filterTags:
pattern: 'v.*'

View File

@@ -0,0 +1,14 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
name: immich-server
namespace: immich
spec:
# https://github.com/immich-app/immich/pkgs/container/immich-server
image: ghcr.io/immich-app/immich-server
interval: 24h
exclusionList:
- pr*
- main
- main*

View File

@@ -0,0 +1,25 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: immich-server
namespace: immich
labels:
app: immich-server
spec:
entryPoints:
- websecure
routes:
- match: Host(`immich.example.cloud`)
kind: Rule
services:
- name: immich-server
port: http
middlewares:
- name: security
namespace: traefik
tls:
secretName: wildcard-example-cloud-cert
options:
name: tls-security
namespace: traefik

View File

@@ -0,0 +1,21 @@
---
apiVersion: v1
kind: Service
metadata:
name: immich-server
namespace: immich
labels:
app: immich-server
spec:
ports:
- name: http
port: 80
targetPort: http
- name: api-metrics
targetPort: 8081
port: 8081
- name: microservices-metrics
targetPort: 8082
port: 8082
selector:
app: immich-server

View File

@@ -0,0 +1,28 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: immich-server
namespace: immich
labels:
app: immich-server
spec:
selector:
matchLabels:
app: immich-server
namespaceSelector:
matchNames:
- immich
endpoints:
- port: api-metrics # port name of service
scheme: http
interval: 15s
metricRelabelings:
- action: labeldrop
regex: (instance|pod)
- port: microservices-metrics # port name of service
scheme: http
interval: 15s
metricRelabelings:
- action: labeldrop
regex: (instance|pod)

View File

@@ -0,0 +1,99 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: immich-server
namespace: immich
annotations:
reloader.stakater.com/auto: 'true'
labels:
app: immich-server
spec:
replicas: 1
selector:
matchLabels:
app: immich-server
template:
metadata:
labels:
app: immich-server
spec:
automountServiceAccountToken: false
containers:
- name: immich-server
image: ghcr.io/immich-app/immich-server:v2.1.0 # {"$imagepolicy": "immich:immich-server"}
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 8Gi
requests:
memory: 1Gi
cpu: 1000m
ports:
- name: http
containerPort: 2283
protocol: TCP
env:
- name: DB_HOSTNAME
valueFrom:
secretKeyRef:
name: immich-db-app
key: host
optional: false
- name: DB_DATABASE_NAME
valueFrom:
secretKeyRef:
name: immich-db-app
key: dbname
optional: false
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: immich-db-app
key: username
optional: false
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: immich-db-app
key: password
optional: false
envFrom:
- configMapRef:
name: immich
optional: false
livenessProbe:
httpGet:
path: /api/server/ping
port: http
failureThreshold: 6
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/server/ping
port: http
failureThreshold: 6
periodSeconds: 10
securityContext:
privileged: true # for intel quick sync
volumeMounts:
- name: immich
mountPath: /data/
- name: immich-upload
mountPath: /data/upload/
- name: synology-photos
mountPath: /mnt/synology-photos/
readOnly: false
nodeSelector:
intel.feature.node.kubernetes.io/gpu: 'true'
volumes:
# unraid NFFS
- name: immich
persistentVolumeClaim:
claimName: immich
- name: immich-upload
persistentVolumeClaim:
claimName: immich-upload
- name: synology-photos
persistentVolumeClaim:
claimName: immich-synology-photos

View File

@@ -0,0 +1,16 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: immich
namespace: immich
labels:
app: immich-server
velero.io/exclude-from-backup: 'true' # because this is nfs storage and velero will try to create snapshots which are will fails
spec:
accessModes:
- ReadWriteMany
storageClassName: k8s-nfs-ssd
resources:
requests:
storage: 500Gi

View File

@@ -0,0 +1,16 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: immich-synology-photos
namespace: immich
labels:
app: immich-server
velero.io/exclude-from-backup: 'true' # because this is nfs storage and velero will try to create snapshots which are will fails
spec:
accessModes:
- ReadWriteMany
storageClassName: k8s-nfs-hdd
resources:
requests:
storage: 100Gi

View File

@@ -0,0 +1,16 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: immich-upload
namespace: immich
labels:
app: immich-server
velero.io/exclude-from-backup: 'true' # because this is nfs storage and velero will try to create snapshots which are will fails
spec:
accessModes:
- ReadWriteMany
storageClassName: k8s-nfs-hdd
resources:
requests:
storage: 2Ti

View File

@@ -0,0 +1,12 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImagePolicy
metadata:
name: immich-valkey
namespace: immich
spec:
imageRepositoryRef:
name: immich-valkey
policy:
semver:
range: '8.*.*'

View File

@@ -0,0 +1,16 @@
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
name: immich-valkey
namespace: immich
spec:
image: docker.io/valkey/valkey
interval: 24h
exclusionList:
- latest
- main
- unstable*
- alpine*
- trixie
- bookworm

View File

@@ -0,0 +1,75 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: immich-valkey
namespace: immich
annotations:
reloader.stakater.com/auto: 'true'
labels:
app: immich-valkey
spec:
replicas: 1
selector:
matchLabels:
app: immich-valkey
template:
metadata:
labels:
app: immich-valkey
spec:
automountServiceAccountToken: false
# securityContext:
# runAsUser: 10001
# runAsGroup: 10001
# fsGroup: 10001
containers:
- name: immich-valkey
# https://hub.docker.com/r/valkey/valkey/
# https://github.com/valkey-io/valkey/releases
image: docker.io/valkey/valkey:8.1.4 # {"$imagepolicy": "immich:immich-valkey"}
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 1Gi
requests:
memory: 100Mi
cpu: 20m
ports:
- name: redis
containerPort: 6379
protocol: TCP
livenessProbe:
exec:
command:
- sh
- -c
- redis-cli ping || exit 1
failureThreshold: 5
initialDelaySeconds: 20
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 6
readinessProbe:
exec:
command:
- sh
- -c
- redis-cli ping || exit 1
failureThreshold: 5
initialDelaySeconds: 20
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 2
securityContext:
privileged: false
seccompProfile:
type: 'RuntimeDefault'
volumeMounts:
- name: immich-valkey
mountPath: /data/
imagePullSecrets:
- name: registry-dockerhub
volumes:
- name: immich-valkey
emptyDir:
sizeLimit: 1Gi

View File

@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: Service
metadata:
name: immich-valkey
namespace: immich
labels:
app: immich-valkey
spec:
ports:
- name: redis
port: 6379
targetPort: redis
selector:
app: immich-valkey