{ description = "Immich - Self-hosted photo and video backup solution (Docker Compose deployment)"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: let # NixOS module to integrate Immich with the system nixosModule = { config, lib, pkgs, ... }: with lib; let cfg = config.services.immich-docker; in { options.services.immich-docker = { enable = mkEnableOption "Immich photo management server (Docker Compose deployment)"; domain = mkOption { type = types.str; default = "immich.local"; description = "Domain name for Immich web interface"; }; port = mkOption { type = types.port; default = 2283; description = "Port for Immich web interface"; }; dataDir = mkOption { type = types.path; default = "/mnt/userdata/immich"; description = "Directory for Immich data"; }; dbPassword = mkOption { type = types.str; default = "immich"; description = "PostgreSQL database password"; }; typesenseApiKey = mkOption { type = types.str; default = "immich-typesense-secret-key-change-this"; description = "Typesense search API key"; }; enableWatchtower = mkOption { type = types.bool; default = false; description = "Enable automatic container updates via Watchtower"; }; }; config = mkIf cfg.enable { # Check for conflicts with the built-in Immich module assertions = [ { assertion = !(config.services.immich.enable or false); message = "services.immich-docker conflicts with services.immich. Please disable the default Nixpkgs Immich module by setting services.immich.enable = false."; } ]; # Ensure Docker is available virtualisation.docker.enable = mkDefault true; # Install docker-compose environment.systemPackages = with pkgs; [ docker-compose ]; # Create required directories systemd.tmpfiles.rules = [ "d /etc/compose/immich 0755 root root -" "d ${cfg.dataDir}/upload 0755 root root -" "d ${cfg.dataDir}/library 0755 root root -" "d ${cfg.dataDir}/postgres 0755 root root -" "d ${cfg.dataDir}/typesense 0755 root root -" ]; # Generate docker-compose configuration environment.etc."compose/immich/docker-compose.yml".text = '' version: "3.8" services: immich_server: image: ghcr.io/immich-app/immich-server:v1.122.2 container_name: immich_server ports: - "${toString cfg.port}:3000" environment: - DB_HOSTNAME=immich_postgres - DB_USERNAME=immich - DB_PASSWORD=${cfg.dbPassword} - DB_DATABASE_NAME=immich - REDIS_HOSTNAME=immich_redis - TYPESENSE_HOST=immich_typesense - IMMICH_WEB_URL=http://localhost:${toString cfg.port} - DISABLE_REVERSE_GEOCODING=false volumes: - ${cfg.dataDir}/upload:/usr/src/app/upload - ${cfg.dataDir}/library:/usr/src/app/library - /etc/localtime:/etc/localtime:ro depends_on: - immich_postgres - immich_redis - immich_typesense restart: unless-stopped ${if cfg.enableWatchtower then ''labels: com.centurylinklabs.watchtower.enable: "true"'' else ""} immich_microservices: image: ghcr.io/immich-app/immich-server:v1.122.2 container_name: immich_microservices command: ["start-microservices.sh"] environment: - DB_HOSTNAME=immich_postgres - DB_USERNAME=immich - DB_PASSWORD=${cfg.dbPassword} - DB_DATABASE_NAME=immich - REDIS_HOSTNAME=immich_redis - TYPESENSE_HOST=immich_typesense - DISABLE_REVERSE_GEOCODING=false volumes: - ${cfg.dataDir}/upload:/usr/src/app/upload - ${cfg.dataDir}/library:/usr/src/app/library - /etc/localtime:/etc/localtime:ro depends_on: - immich_postgres - immich_redis - immich_typesense restart: unless-stopped ${if cfg.enableWatchtower then ''labels: com.centurylinklabs.watchtower.enable: "true"'' else ""} immich_postgres: image: tensorchord/pgvecto-rs:pg14-v0.2.0 container_name: immich_postgres environment: - POSTGRES_USER=immich - POSTGRES_PASSWORD=${cfg.dbPassword} - POSTGRES_DB=immich volumes: - ${cfg.dataDir}/postgres:/var/lib/postgresql/data restart: unless-stopped ${if cfg.enableWatchtower then ''labels: com.centurylinklabs.watchtower.enable: "true"'' else ""} immich_redis: image: redis:7-alpine container_name: immich_redis restart: unless-stopped ${if cfg.enableWatchtower then ''labels: com.centurylinklabs.watchtower.enable: "true"'' else ""} immich_typesense: image: typesense/typesense:0.24.0 container_name: immich_typesense environment: - TYPESENSE_API_KEY=${cfg.typesenseApiKey} - TYPESENSE_DATA_DIR=/data volumes: - ${cfg.dataDir}/typesense:/data restart: unless-stopped ${if cfg.enableWatchtower then ''labels: com.centurylinklabs.watchtower.enable: "true"'' else ""} networks: default: name: immich_network ''; # Systemd service to manage Immich docker-compose stack systemd.services.docker-compose-immich = { description = "Immich Docker Compose Stack"; after = [ "docker.service" "network-online.target" ]; wants = [ "network-online.target" ]; requiredBy = [ "multi-user.target" ]; path = [ pkgs.docker-compose pkgs.docker ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = "yes"; WorkingDirectory = "/etc/compose/immich"; ExecStart = "${pkgs.docker-compose}/bin/docker-compose up -d"; ExecStop = "${pkgs.docker-compose}/bin/docker-compose down"; }; }; # Network firewall configuration (optional, enable if using firewall) # networking.firewall.allowedTCPPorts = [ cfg.port ]; }; }; in { # Provide NixOS module nixosModules.immich-docker = nixosModule; # Provide the module as the default package packages = flake-utils.lib.eachDefaultSystem (system: { immich-docker-module = nixosModule; default = nixosModule; } ); }; }