From 12bb7b0eacbb2a58582810f4f72c84b45482a6e4 Mon Sep 17 00:00:00 2001 From: Julian Sutter Date: Mon, 16 Feb 2026 21:51:05 -0800 Subject: [PATCH] Add Immich server configuration --- flake.lock | 6 +- flake.nix | 1 + servers/immich.nix | 165 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 servers/immich.nix diff --git a/flake.lock b/flake.lock index 9f1b917..8dd7d52 100755 --- a/flake.lock +++ b/flake.lock @@ -39,11 +39,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1770136044, - "narHash": "sha256-tlFqNG/uzz2++aAmn4v8J0vAkV3z7XngeIIB3rM3650=", + "lastModified": 1771208521, + "narHash": "sha256-X01Q3DgSpjeBpapoGA4rzKOn25qdKxbPnxHeMLNoHTU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e576e3c9cf9bad747afcddd9e34f51d18c855b4e", + "rev": "fa56d7d6de78f5a7f997b0ea2bc6efd5868ad9e8", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8f5fbd6..93de189 100755 --- a/flake.nix +++ b/flake.nix @@ -87,6 +87,7 @@ ./systems/warp.nix ./servers/nginx.nix ./servers/forgejo.nix + ./servers/immich.nix ]; }; skip = mkSystem { diff --git a/servers/immich.nix b/servers/immich.nix new file mode 100644 index 0000000..4d0ce8c --- /dev/null +++ b/servers/immich.nix @@ -0,0 +1,165 @@ +{ 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://localhost:${toString immichPort}"; + proxyWebsockets = true; + recommendedProxySettings = true; + + # Extra websocket configuration + extraConfig = '' + # WebSocket upgrade headers + proxy_http_version 1.1; + 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 + }; + }; + }; + + # Create borg backup directory with proper permissions + system.activationScripts.immich-borg-dir = '' + mkdir -p ${borgRepo} + chown -R borg:borg ${borgRepo} + chmod 700 ${borgRepo} + ''; + + # 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; + }; +}