Post

Immich - Selfhosted AI Powered Photo/Video Management

Immich - Selfhosted AI Powered Photo/Video Management

Immich Self-hosted photo and video management solution

Since this uses media from my NAS i have simply decided to run this as a Docker Container on UnRAID on my NAS instead of having it as part of my kubernetes cluster to remove the need to access the NAS media remotely

Kubernetes Manifest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: immich
    app.kubernetes.io/instance: immich
    app.kubernetes.io/name: immich
  name: immich
  namespace: immich
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: immich
  template:
    metadata:
      labels:
        app: immich
        app.kubernetes.io/name: immich
    spec:
      nodeSelector:
        nas: "true"
      containers:
      - image: ghcr.io/imagegenius/immich:latest
        name: immich
        securityContext:
            runAsUser: 1007
            runAsGroup: 100
        ports:
          - containerPort: 8080
            hostPort: 8080
            protocol: TCP
        env:
          - name: DB_DATABASE_NAME
            value: immich
          - name: DB_HOSTNAME
            value: postgresql.postgresql
          - name: DB_PASSWORD
            value: [REDACTED]
          - name: DB_USERNAME
            value: immich
          - name: REDIS_HOSTNAME
            value: redis.redis
          - name: REDIS_PASSWORD
            value: [REDACTED]
          - name: REDIS_PORT
            value: "6379"
          - name: TZ
            value: Europe/London
        volumeMounts:
        - mountPath: "/photos"
          readOnly: false
          name: nfs
          subPath: "Photos"
        - mountPath: "/config"
          readOnly: false
          name: config
        - mountPath: /dev/dri
          name: dri
        securityContext:
          privileged: true
      volumes:
        - name: nfs
          persistentVolumeClaim:
            claimName: pvc-immich-nfs
        - name: config
          persistentVolumeClaim:
            claimName: immich
        - name: dri
          hostPath:
            path: /dev/dri
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: immich
  name: immich
  namespace: immich 
spec:
  ports:
  - name: web-tcp
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: immich
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: immich
  namespace: immich
  annotations: 
    kubernetes.io/ingress.class: traefik-external
    gethomepage.dev/href: "https://photos.f9.casa"
    gethomepage.dev/enabled: "true"
    gethomepage.dev/description: Image Library
    gethomepage.dev/group: Media
    gethomepage.dev/icon: immich.png
    gethomepage.dev/name: Immich
    gethomepage.dev/widget.type: immich
    gethomepage.dev/widget.url: "http://immich.immich:8080"
    gethomepage.dev/widget.key: "[REDACTED]"
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`photos.f9.casa`)
      kind: Rule
      services:
        - name: immich
          port: 8080
      middlewares:
        - name: default-headers
          namespace: default
  tls:
    secretName: f9-casa-tls

Docker Compose

Note that as I use Docker Swarm I am making use of https://github.com/allfro/device-mapping-manager to pass through dev’s to swarm containers by faking a volume, this container then detects that fake volume and maps it as a device

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
version: '3.9'
services:
  immich:
    image: ghcr.io/imagegenius/immich:latest
    hostname: immich
    privileged: true
    networks:
      - traefik-public
    environment:
      - DB_PASSWORD=[REDACTED]
      - DB_HOSTNAME=postgresql
      - DB_USERNAME=immich
      - DB_DATABASE_NAME=immich
      - REDIS_HOSTNAME=redis
      - REDIS_PORT=6379
      - REDIS_PASSWORD=[REDACTED]
      - PUID=1000
      - PGID=1000
      - TZ=Europe/London
    volumes:
      - /srv/cephfs/docker/appdata/immich:/config
      - /srv/photos:/photos
      - /dev/dri:/dev/dri
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.immich.rule=Host(`photos.f9.casa`)"
        - "traefik.http.services.immich.loadbalancer.server.port=8080"
        - "traefik.http.routers.immich.entrypoints=websecure"
        - "traefik.http.routers.immich.tls=true"
        - "traefik.http.routers.immich.tls.certresolver=letsencrypt"

        - homepage.group=Media
        - homepage.name=Immich
        - homepage.icon=immich.png
        - homepage.href=https://photos.f9.casa
        - homepage.description=Image Library
        - homepage.siteMonitor=http://immich:8080
        - homepage.widget.type=immich
        - homepage.widget.url=http://immich:8080
        - homepage.widget.key=[REDACTED]
        
      mode: replicated
      placement:
        constraints:
          - node.labels.gpu == true
networks:
  traefik-public:
    external: true
This post is licensed under CC BY 4.0 by the author.