From 532e01cbf12f58daa576dea91fd65b4cec6544c6 Mon Sep 17 00:00:00 2001 From: Julian Sutter Date: Tue, 3 Feb 2026 23:35:34 -0800 Subject: [PATCH] feat: Add Immich app flake with docker-compose deployment - Create Immich module as appflake in appflakes/immich/ - Module provides NixOS module for docker-compose deployment - Includes 5-container stack: * immich_server: Web API and interface * immich_microservices: Background jobs and ML processing * immich_postgres: PostgreSQL with pgvector * immich_redis: Redis cache * immich_typesense: Search engine - Configuration options for: * Domain and port customization * Data directory location * Database and API keys * Optional Watchtower auto-updates - Automatic systemd service integration - Comprehensive documentation in README.md - Named module 'immich-docker' to avoid conflicts with Nixpkgs module --- appflakes/immich/README.md | 356 ++++++++++++++++++++++++++++++++++++ appflakes/immich/flake.lock | 61 ++++++ appflakes/immich/flake.nix | 209 +++++++++++++++++++++ 3 files changed, 626 insertions(+) create mode 100644 appflakes/immich/README.md create mode 100644 appflakes/immich/flake.lock create mode 100644 appflakes/immich/flake.nix diff --git a/appflakes/immich/README.md b/appflakes/immich/README.md new file mode 100644 index 0000000..a345236 --- /dev/null +++ b/appflakes/immich/README.md @@ -0,0 +1,356 @@ +# Immich NixOS Module + +Self-hosted photo and video backup solution packaged as a NixOS module. + +## About Immich + +Immich is a high-performance self-hosted photo and video backup solution. This module deploys Immich using Docker Compose with automatic service management. + +**Key Features:** +- Automatic backup from mobile devices +- AI-powered face recognition +- Smart search with object and location detection +- Sharing albums with family and friends +- Timeline and map views +- Duplicate detection +- Video transcoding and playback + +## Usage + +### Basic Configuration + +Add this module to your NixOS configuration: + +```nix +{ config, pkgs, ... }: +{ + imports = [ + # Import the Immich Docker Compose module + (builtins.getFlake "path:/path/to/appflakes/immich").nixosModules.immich-docker + ]; + + services.immich-docker = { + enable = true; + domain = "immich.example.com"; + port = 2283; + dataDir = "/mnt/userdata/immich"; + }; +} +``` + +### With Flake Inputs + +If using flakes, add Immich as an input: + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + immich = { + url = "path:./appflakes/immich"; + }; + }; + + outputs = { self, nixpkgs, immich, ... }: { + nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem { + modules = [ + immich.nixosModules.immich-docker + ./configuration.nix + ]; + }; + }; +} +``` + +### Example for Warp Server + +```nix +{ config, ... }: + +{ + imports = [ + (builtins.getFlake "path:${self}/appflakes/immich").nixosModules.immich-docker + ]; + + services.immich-docker = { + enable = true; + domain = "warp.example.com"; + port = 2283; + dataDir = "/mnt/userdata/immich"; + dbPassword = "your-secure-password-here"; + typesenseApiKey = "your-typesense-key-here"; + enableWatchtower = true; + }; +} +``` + +## Configuration Options + +### `services.immich-docker.enable` (boolean) +**Default:** `false` + +Enable the Immich Docker Compose service. + +### `services.immich-docker.domain` (string) +**Default:** `"immich.local"` + +The domain name for the Immich web interface. + +### `services.immich-docker.port` (port) +**Default:** `2283` + +The port number for the Immich web interface. + +### `services.immich-docker.dataDir` (path) +**Default:** `"/mnt/userdata/immich"` + +Directory where Immich will store all data including: +- Uploaded photos and videos +- Processed images +- Database files +- Search indexes + +### `services.immich-docker.dbPassword` (string) +**Default:** `"immich"` + +PostgreSQL database password. **Change this in production!** + +### `services.immich-docker.typesenseApiKey` (string) +**Default:** `"immich-typesense-secret-key-change-this"` + +API key for the Typesense search engine. **Change this in production!** + +### `services.immich-docker.enableWatchtower` (boolean) +**Default:** `false` + +Enable automatic container updates via Watchtower. When enabled, Watchtower will update containers when new versions are available. + +## Deployment + +### Automatic Deployment + +The NixOS module handles everything automatically: + +1. Creates required directories +2. Generates docker-compose configuration +3. Configures systemd service +4. Starts all containers on boot + +### Manual Service Management + +```bash +# Start Immich +sudo systemctl start docker-compose-immich + +# Stop Immich +sudo systemctl stop docker-compose-immich + +# Restart Immich +sudo systemctl restart docker-compose-immich + +# Check status +sudo systemctl status docker-compose-immich + +# View logs +sudo journalctl -u docker-compose-immich -f +``` + +### Container Management + +```bash +# View all Immich containers +docker ps | grep immich + +# View logs for specific service +docker logs immich_server + +# Execute commands in container +docker exec -it immich_server bash +``` + +## Architecture + +This module deploys Immich as a Docker Compose stack with the following containers: + +- **immich_server**: Web API and interface +- **immich_microservices**: Background jobs and machine learning +- **immich_postgres**: PostgreSQL database with pgvector +- **immich_redis**: Redis cache +- **immich_typesense**: Search engine + +The stack is managed by systemd and starts automatically on boot. + +## Access + +Once deployed, access Immich at: + +- **Web Interface:** `http://your-domain:port` (default: `http://localhost:2283`) +- **Mobile App:** Configure using your domain and port + +## Storage Requirements + +Plan for significant storage based on your photo/video collection: + +- **High-res photos:** 5-15 MB per photo +- **Processed versions:** 2-5 MB additional per photo +- **Thumbnails:** ~500 KB per photo +- **4K videos:** 100-500 MB per minute +- **Database:** ~100-500 MB for 10,000 photos + +**Recommended:** +- Start with at least 100GB for small collections +- 500GB+ for growing collections +- 1TB+ for serious photographers + +## Security Recommendations + +1. **Change Default Passwords**: Update `dbPassword` and `typesenseApiKey` in your NixOS configuration +2. **Reverse Proxy:** Use Nginx with SSL/TLS for production +3. **Firewall:** Restrict access to port 2283 +4. **Backups:** Regularly backup the `dataDir` +5. **Updates**: Keep Immich containers updated (enable Watchtower or update the image tag in the module) +6. **Network:** Consider VPN access for remote management + +## Backup Strategy + +### Automated Backup Example + +```nix +{ config, pkgs, ... }: + +{ + services.immich-docker.enable = true; + + # Example: Regular backups using restic + services.restic.backups.immich = { + paths = [ "/mnt/userdata/immich" ]; + repository = "s3:backup-bucket/immich"; + passwordFile = "/etc/restic/password"; + timerConfig = { + OnCalendar = "daily"; + Persistent = true; + }; + }; +} +``` + +### Important Backup Locations + +Ensure these are included in backups: +- `${dataDir}/upload/` - Original photos and videos +- `${dataDir}/postgres/` - Database +- `${dataDir}/typesense/` - Search indexes + +## Troubleshooting + +### Service Won't Start + +```bash +# Check service logs +sudo journalctl -u docker-compose-immich -n 100 + +# Check Docker status +sudo systemctl status docker + +# Verify directories exist +ls -la /mnt/userdata/immich +``` + +### Database Connection Issues + +```bash +# Check PostgreSQL container +docker logs immich_postgres + +# Test database connectivity +docker exec -it immich_postgres psql -U immich -d immich +``` + +### Performance Issues + +- Ensure adequate CPU (4+ cores recommended) +- Allocate sufficient RAM (8GB+ recommended) +- Monitor Docker resource usage +- Check storage space: `df -h /mnt/userdata` + +### Port Already In Use + +If port 2283 is already in use, change the port: + +```nix +services.immich.port = 8080; # Use alternative port +``` + +## Migration + +### From Manual Docker Compose + +1. Stop existing containers +2. Backup current data directory +3. Update NixOS configuration to use this module +4. Set `dataDir` to your current data directory +5. Apply new configuration +6. Start service: `sudo systemctl start docker-compose-immich` + +## Version Updates + +### Manual Updates + +Update the Immich version by modifying the image tag in the flake: + +```nix +# In flake.nix, change: +image: ghcr.io/immich-app/immich-server:v1.122.2 + +# To: +image: ghcr.io/immich-app/immich-server:v1.123.0 +``` + +Then rebuild the NixOS configuration. + +### Automatic Updates + +Enable Watchtower for automatic updates: + +```nix +services.immich = { + enable = true; + enableWatchtower = true; # Enables automatic container updates +}; +``` + +## Resources + +- [Immich Official Website](https://immich.app) +- [Immich Documentation](https://immich.app/docs) +- [Immich GitHub](https://github.com/immich-app/immich) +- [Docker Compose Reference](https://docs.docker.com/compose/) + +## Support + +For Immich-specific issues, refer to: +- [Immich GitHub Issues](https://github.com/immich-app/immich/issues) +- [Immich Discord Community](https://discord.gg/DSbk7SPrCj) + +For NixOS module issues, check: +- Module configuration and syntax +- Docker integration +- Systemd service logs + +## Version Updates + +To update Immich to a newer version, modify the image tag in the flake: + +```nix +# In appflakes/immich/flake.nix, change: +image: ghcr.io/immich-app/immich-server:v1.122.2 + +# To: +image: ghcr.io/immich-app/immich-server:v1.123.0 +``` + +Then rebuild your NixOS configuration. + +## License + +This NixOS module follows the same license as the Immich project (AGPL-3.0). \ No newline at end of file diff --git a/appflakes/immich/flake.lock b/appflakes/immich/flake.lock new file mode 100644 index 0000000..668ac53 --- /dev/null +++ b/appflakes/immich/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1770115704, + "narHash": "sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e6eae2ee2110f3d31110d5c222cd395303343b08", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/appflakes/immich/flake.nix b/appflakes/immich/flake.nix new file mode 100644 index 0000000..2854894 --- /dev/null +++ b/appflakes/immich/flake.nix @@ -0,0 +1,209 @@ +{ + 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; + } + ); + }; +}