{ lib, pkgs, config, ... }: let cfg = config.services.immich; # External domain for Immich fqdn = "photos.symbiotrip.com"; # Immich service port (default) immichPort = 2283; # Immich media storage location mediaLocation = "/var/lib/immich/media"; # Borg backup repository path borgRepo = "/var/backups/immich-borg"; in { # DNS-based ACME certificate configuration # Uses defaults (dnsProvider, email, credentials) from nginx.nix security.acme.certs.${fqdn} = { # Group needs to be set so nginx can read the certificate group = config.services.nginx.group; # Inherit DNS challenge configuration from security.acme.defaults (set in nginx.nix) # This includes: dnsProvider = "cloudflare", environmentFile with Cloudflare token, email # Explicitly ensure DNS mode (not HTTP-01) webroot = null; }; # Nginx reverse proxy for Immich services.nginx.virtualHosts.${fqdn} = { # Use DNS-based ACME certificate instead of HTTP-01 challenge enableACME = false; useACMEHost = fqdn; forceSSL = true; extraConfig = '' # Large upload limit for photos and videos (50GB max) client_max_body_size 50000M; # Proxy headers proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Extended timeouts for large file uploads client_body_timeout 600s; client_header_timeout 600s; proxy_read_timeout 600s; proxy_send_timeout 600s; send_timeout 600s; # Logging for debugging access_log /var/log/nginx/${fqdn}-access.log; error_log /var/log/nginx/${fqdn}-error.log warn; ''; locations."/" = { proxyPass = "http://127.0.0.1:${toString immichPort}"; proxyWebsockets = true; recommendedProxySettings = true; # Extra websocket configuration extraConfig = '' proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; ''; }; }; # Immich service configuration (NixOS native) services.immich = { enable = true; # Bind to localhost only - nginx handles public HTTPS host = "127.0.0.1"; port = immichPort; # Media storage location mediaLocation = mediaLocation; # Firewall - nginx handles external access openFirewall = false; }; # Hardware acceleration for video transcoding # This leverages Intel Quick Sync Video on the NUC's Intel GPU services.immich.accelerationDevices = null; # Enable graphics acceleration hardware.graphics = { enable = true; enable32Bit = true; extraPackages = with pkgs; [ intel-media-driver # Intel Quick Sync Video support ]; }; # Add immich user to video and render groups for GPU access users.users.immich.extraGroups = [ "video" "render" ]; # Ensure nginx starts after ACME certificate is available systemd.services.nginx = { after = [ "acme-${fqdn}.service" "acme-${fqdn}-renew.service" ]; requires = [ "acme-${fqdn}.service" ]; }; # PostgreSQL database backups for Immich services.postgresqlBackup = { enable = true; databases = [ "immich" ]; location = "/var/backups/postgresql/immich"; startAt = "daily"; compression = "zstd"; }; # Borgbackup for Immich media files # Manual repository initialization required: # sudo -u borg borg init --encryption=repokey-blake2 ${borgRepo} services.borgbackup.jobs."immich" = { paths = [ mediaLocation ]; repo = borgRepo; # Run backup daily at 3 AM startAt = "*-*-* 03:00:00"; compression = "auto,zstd,6"; encryption.mode = "none"; # Repository handles encryption prune = { keep = { within = "1d"; # Keep all backups from the last day daily = 7; # Keep last 7 daily backups weekly = 4; # Keep last 4 weekly backups monthly = 6; # Keep last 6 monthly backups }; }; }; # Note: borgbackup service handles directory creation automatically # Create Immich media directory with proper permissions system.activationScripts.immich-media-dir = '' mkdir -p ${mediaLocation} chown -R immich:immich ${mediaLocation} chmod 755 ${mediaLocation} ''; # Systemd service to monitor disk usage services.prometheus.exporters.node = { enable = true; enabledCollectors = [ "systemd" "textfile" "filesystem" ]; port = 9100; }; }