From b6bafc3d3f9eb9ed3bb7cc61b136225ad83f3e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Thu, 2 Apr 2026 17:48:22 -0700 Subject: [PATCH 1/2] feat(glyph): add n8n workflow automation with PostgreSQL Set up n8n on glyph backed by PostgreSQL instead of SQLite, with reverse proxy on spore at n8n.zx.dev. Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/db.nix | 8 ++++++-- hosts/glyph/services/default.nix | 1 + hosts/glyph/services/n8n.nix | 24 ++++++++++++++++++++++++ hosts/spore/services/web/default.nix | 8 ++++++++ modules/base/unfree-packages.nix | 1 + 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 hosts/glyph/services/n8n.nix diff --git a/hosts/glyph/services/db.nix b/hosts/glyph/services/db.nix index bc35dfb5..e8be4d4b 100644 --- a/hosts/glyph/services/db.nix +++ b/hosts/glyph/services/db.nix @@ -12,7 +12,7 @@ port = 5432; max_connections = 150; }; - ensureDatabases = ["atticd" "grafana" "open-webui" "pocketid"]; + ensureDatabases = ["atticd" "grafana" "n8n" "open-webui" "pocketid"]; ensureUsers = [ { name = "mu"; @@ -26,6 +26,10 @@ name = "grafana"; ensureDBOwnership = true; } + { + name = "n8n"; + ensureDBOwnership = true; + } { name = "open-webui"; ensureDBOwnership = true; @@ -39,6 +43,6 @@ services.postgresqlBackup = { enable = true; - databases = ["atticd" "grafana" "open-webui" "pocketid"]; + databases = ["atticd" "grafana" "n8n" "open-webui" "pocketid"]; }; } diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 5bcb18f5..5fbf5e39 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -12,6 +12,7 @@ ./filebrowser.nix ./jellyfin.nix ./loki.nix + ./n8n.nix ./nfs.nix ./open-terminal.nix ./open-webui.nix diff --git a/hosts/glyph/services/n8n.nix b/hosts/glyph/services/n8n.nix new file mode 100644 index 00000000..53f3aa8e --- /dev/null +++ b/hosts/glyph/services/n8n.nix @@ -0,0 +1,24 @@ +{config, ...}: { + services.n8n = { + enable = true; + environment = { + N8N_PORT = 5678; + GENERIC_TIMEZONE = "America/Los_Angeles"; + N8N_VERSION_NOTIFICATIONS_ENABLED = false; + N8N_DIAGNOSTICS_ENABLED = false; + WEBHOOK_URL = "https://n8n.zx.dev"; + + # PostgreSQL via unix socket + DB_TYPE = "postgresdb"; + DB_POSTGRESDB_HOST = "/run/postgresql"; + DB_POSTGRESDB_DATABASE = "n8n"; + DB_POSTGRESDB_USER = "n8n"; + }; + }; + + systemd.services.n8n = { + after = ["postgresql.service"]; + requires = ["postgresql.service"]; + serviceConfig.SupplementaryGroups = ["postgres"]; + }; +} diff --git a/hosts/spore/services/web/default.nix b/hosts/spore/services/web/default.nix index aa91a528..3ea52cf5 100644 --- a/hosts/spore/services/web/default.nix +++ b/hosts/spore/services/web/default.nix @@ -45,6 +45,14 @@ "/pgp".return = "302 https://keyoxide.org/hkp/413d1a0152bcb08d2e3ddacaf88c08579051ab48"; }; }; + "n8n.zx.dev" = { + forceSSL = true; + useACMEHost = "zx.dev"; + locations."/" = { + proxyPass = "http://glyph.rove-duck.ts.net:5678"; + proxyWebsockets = true; + }; + }; "torrents.zx.dev" = { forceSSL = true; useACMEHost = "zx.dev"; diff --git a/modules/base/unfree-packages.nix b/modules/base/unfree-packages.nix index 89c20fda..aee40623 100644 --- a/modules/base/unfree-packages.nix +++ b/modules/base/unfree-packages.nix @@ -12,6 +12,7 @@ "fastscripts" "graphite-cli" "mochi" + "n8n" "open-webui" "roon-server" "soundsource" From fe3b72e3b60e1db002371bb48818310b55ab8f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Thu, 2 Apr 2026 17:54:49 -0700 Subject: [PATCH 2/2] feat(glyph): add n8n encryption key via agenix Persist the encryption key used for n8n credential storage so it survives rebuilds and can decrypt credentials from DB backups. Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/secrets/n8n-encryption-key.age | 8 ++++++++ hosts/glyph/services/n8n.nix | 4 ++++ lib/secrets/glyph.nix | 1 + 3 files changed, 13 insertions(+) create mode 100644 hosts/glyph/secrets/n8n-encryption-key.age diff --git a/hosts/glyph/secrets/n8n-encryption-key.age b/hosts/glyph/secrets/n8n-encryption-key.age new file mode 100644 index 00000000..0464935e --- /dev/null +++ b/hosts/glyph/secrets/n8n-encryption-key.age @@ -0,0 +1,8 @@ +age-encryption.org/v1 +-> ssh-ed25519 rSr+rA 3Wam+tzyqvZtAii2UFkwt8puuyh8VaS4hO9N7acCUQA +kgO1pA6wo0IZxluR16exJmTmTAN2kfXlJlkwa6LI4D8 +-> ssh-ed25519 3EWhnQ zanilw3QBMGYkhsXxMoByYNfeoj2g5b+gPULbSq/PGU +4WP+XEH7XvQx/cIz98X1SyNtEseifek7rc91o8h9xQI +--- GytTZNL2/8wEusnL6byCRPjgTWjlbQeA+H5+vF4Oezw +y0Ò#ŽoðͲÞf#/²”?ÙDMÂÙÏ!Èã—ÃRÈ}ìàe— +ÂG(•:†t\?[8å]Tç gÍì¡Í!ßë…Ÿ0([MûW=lY¨)–Ò˜ÒùàÐLœ& 4á­Î \ No newline at end of file diff --git a/hosts/glyph/services/n8n.nix b/hosts/glyph/services/n8n.nix index 53f3aa8e..b076bfec 100644 --- a/hosts/glyph/services/n8n.nix +++ b/hosts/glyph/services/n8n.nix @@ -1,4 +1,6 @@ {config, ...}: { + age.secrets.n8n-encryption-key.file = ./../secrets/n8n-encryption-key.age; + services.n8n = { enable = true; environment = { @@ -7,6 +9,7 @@ N8N_VERSION_NOTIFICATIONS_ENABLED = false; N8N_DIAGNOSTICS_ENABLED = false; WEBHOOK_URL = "https://n8n.zx.dev"; + N8N_ENCRYPTION_KEY_FILE = config.age.secrets.n8n-encryption-key.path; # PostgreSQL via unix socket DB_TYPE = "postgresdb"; @@ -20,5 +23,6 @@ after = ["postgresql.service"]; requires = ["postgresql.service"]; serviceConfig.SupplementaryGroups = ["postgres"]; + restartTriggers = [config.age.secrets.n8n-encryption-key.file]; }; } diff --git a/lib/secrets/glyph.nix b/lib/secrets/glyph.nix index a178af8c..ec0dceb6 100644 --- a/lib/secrets/glyph.nix +++ b/lib/secrets/glyph.nix @@ -12,4 +12,5 @@ in { "hosts/glyph/secrets/grafana-mcp-token.age".publicKeys = keys; "hosts/glyph/secrets/graphite-auth-token.age".publicKeys = keys; "hosts/glyph/secrets/attic-credentials.age".publicKeys = keys; + "hosts/glyph/secrets/n8n-encryption-key.age".publicKeys = keys; }