{ config, pkgs, lib, ... }: with lib; let # Container configuration name = "immich"; # Version tagging immichVersion = "v2.5.6"; redisVersion = "9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63"; databaseVersion = "14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23"; # Helper helper = import ../service-helper.nix { inherit config pkgs lib; }; cfg = config.numbus-server.services.immich; in helper.mkPodmanService { inherit name; description = "Immich, Google Photos but better"; defaultPort = "2283"; middlewares = [ "immichSecureHeaders" ]; dirPermissions = [ "100999:100 ${cfg.configDir}" "100999:100 ${cfg.configDir}/redis" "100999:100 ${cfg.configDir}/model-cache" "100999:100 ${cfg.configDir}/machine-learning-cache" "100999:100 ${cfg.configDir}/machine-learning-config" "100999:100 ${cfg.configDir}/database" "100999:100 ${cfg.dataDir}" ]; secrets = [ "immich/redis_hostname" "immich/db_hostname" "immich/db_name" "immich/db_username" "immich/db_password" ]; # Compose file good composeText = '' services: immich-server: container_name: immich-server hostname: immich-server image: ghcr.io/immich-app/immich-server:${immichVersion} user: '1000:1000' networks: immich: ipv4_address: 10.89.210.253 ports: - "${cfg.port}:2283/tcp" volumes: - $UPLOAD_LOCATION:/data - /etc/localtime:/etc/localtime:ro environment: TZ: $TZ REDIS_HOSTNAME: ${config.sops.placeholder."immich/redis_hostname"} DB_HOSTNAME: ${config.sops.placeholder."immich/db_hostname"} DB_DATABASE_NAME: ${config.sops.placeholder."immich/db_name"} DB_USERNAME: ${config.sops.placeholder."immich/db_username"} DB_PASSWORD: ${config.sops.placeholder."immich/db_password"} IMMICH_TRUSTED_PROXIES: 10.89.210.1 depends_on: - immich-redis - immich-database healthcheck: disable: false security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped immich-machine-learning: container_name: immich-machine-learning hostname: immich-machine-learning image: ghcr.io/immich-app/immich-machine-learning:${immichVersion} user: '1000:1000' networks: immich: ipv4_address: 10.89.210.252 volumes: - ${cfg.configDir}/model-cache:/cache - ${cfg.configDir}/machine-learning-config:/usr/src/.config - ${cfg.configDir}/machine-learning-cache:/usr/src/.cache/ healthcheck: disable: false security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped immich-redis: container_name: immich-redis hostname: immich-redis image: docker.io/valkey/valkey:${redisVersion} user: '1000:1000' networks: immich: ipv4_address: 10.89.210.251 volumes: - ${cfg.configDir}/redis:/data healthcheck: test: redis-cli ping || exit 1 security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped immich-database: container_name: immich-database hostname: immich-database image: ghcr.io/immich-app/postgres:${databaseVersion} user: '1000:1000' networks: immich: ipv4_address: 10.89.210.250 environment: POSTGRES_DB: ${config.sops.placeholder."immich/db_name"} POSTGRES_USER: ${config.sops.placeholder."immich/db_username"} POSTGRES_PASSWORD: ${config.sops.placeholder."immich/db_password"} POSTGRES_INITDB_ARGS: '--data-checksums' volumes: - $DB_DATA_LOCATION:/var/lib/postgresql/data shm_size: 128mb healthcheck: disable: false security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped networks: immich: driver: bridge name: immich ipam: config: - subnet: "10.89.210.0/24" gateway: "10.89.210.254" ''; extraConfig = { sops.templates."immich/env" = { gid = "100"; uid = "1000"; mode = "0400"; content = '' DB_DATA_LOCATION=${cfg.configDir}/database UPLOAD_LOCATION=${cfg.dataDir} ''; path = "/etc/podman/immich/.env"; }; sops.templates."traefik/rules/immich-secureHeaders" = { gid = "100"; uid = "100999"; mode = "0400"; content = '' http: middlewares: immichSecureHeaders: headers: FrameDeny: true AccessControlAllowMethods: 'GET,POST,PUT,DELETE,OPTIONS' AccessControlAllowOriginList: - https://${cfg.subdomain}.${config.numbus-server.services.domain} - origin-list-or-null AccessControlMaxAge: 100 AddVaryHeader: true BrowserXssFilter: true ContentTypeNosniff: true ForceSTSHeader: true STSIncludeSubdomains: true STSPreload: true ContentSecurityPolicy: "default-src 'self'; base-uri 'self'; img-src 'self' https://static.immich.cloud https://tiles.immich.cloud data: blob:; connect-src 'self' https://${cfg.subdomain}.${config.numbus-server.services.domain} wss://${cfg.subdomain}.${config.numbus-server.services.domain} https://static.immich.cloud https://tiles.immich.cloud; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; script-src 'self' 'unsafe-inline' 'unsafe-eval'; worker-src 'self' blob: https://${cfg.subdomain}.${config.numbus-server.services.domain}; frame-ancestors 'self';" CustomFrameOptionsValue: SAMEORIGIN ReferrerPolicy: same-origin PermissionsPolicy: vibrate 'self' STSSeconds: 315360000 ''; path = "/etc/traefik/rules/immich-secureHeaders.yaml"; }; }; }