From d663ca36b8c016bfe058061c8f4356f7e747e079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 21:12:32 -0700 Subject: [PATCH 1/6] feat: centralize PostgreSQL on glyph Move PostgreSQL from spore to glyph so all databases are on the host with the best hardware (i7-13700K, NVMe, ZFS). This also fixes the atticd SQLite connection pool timeout under concurrent CI pushes by switching to PostgreSQL. - Add PostgreSQL 16 on glyph with atticd and pocketid databases - Switch atticd from SQLite to PostgreSQL - Add databaseURL and localDatabase options to rc.web.auth module - Configure pocket-id on spore to connect to glyph via Tailscale - Disable PostgreSQL on spore - Move database backups from spore to glyph Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/attic.nix | 7 +++- hosts/glyph/services/db.nix | 32 ++++++++++++++++++ hosts/glyph/services/default.nix | 3 +- hosts/spore/backup.nix | 6 ++-- hosts/spore/services/db.nix | 23 ++----------- hosts/spore/services/mastodon.nix | 16 ++------- hosts/spore/services/web/auth.nix | 2 ++ modules/nixos/web/auth.nix | 56 +++++++++++++++++++++---------- 8 files changed, 88 insertions(+), 57 deletions(-) create mode 100644 hosts/glyph/services/db.nix diff --git a/hosts/glyph/services/attic.nix b/hosts/glyph/services/attic.nix index 19e2d1ae..b855e189 100644 --- a/hosts/glyph/services/attic.nix +++ b/hosts/glyph/services/attic.nix @@ -6,6 +6,11 @@ group = "atticd"; }; + systemd.services.atticd = { + after = ["postgresql.service"]; + requires = ["postgresql.service"]; + }; + services.atticd = { enable = true; environmentFile = config.age.secrets.attic-credentials.path; @@ -13,7 +18,7 @@ settings = { listen = "[::]:8199"; - database.url = "sqlite:///var/lib/atticd/server.db?mode=rwc"; + database.url = "postgresql:///atticd?host=/run/postgresql"; storage = { type = "local"; diff --git a/hosts/glyph/services/db.nix b/hosts/glyph/services/db.nix new file mode 100644 index 00000000..21a22c95 --- /dev/null +++ b/hosts/glyph/services/db.nix @@ -0,0 +1,32 @@ +{pkgs, ...}: { + services.postgresql = { + enable = true; + package = pkgs.postgresql_16; + enableTCPIP = true; + authentication = pkgs.lib.mkOverride 10 '' + local all all trust + host all all 127.0.0.1/32 trust + host all all 100.64.0.0/10 trust + ''; + settings = { + port = 5432; + max_connections = 150; + }; + ensureDatabases = ["atticd" "pocketid"]; + ensureUsers = [ + { + name = "atticd"; + ensureDBOwnership = true; + } + { + name = "pocketid"; + ensureDBOwnership = true; + } + ]; + }; + + services.postgresqlBackup = { + enable = true; + databases = ["atticd" "pocketid"]; + }; +} diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index be23d643..860b1cb4 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -5,6 +5,7 @@ }: { imports = [ ./attic.nix + ./db.nix ./avahi.nix ./dns.nix ./filebrowser.nix @@ -85,7 +86,7 @@ rc.backup = { enable = true; paths = [ - "/var/lib/atticd/server.db" + config.services.postgresqlBackup.location "/var/lib/basic-memory" "/var/lib/open-webui" "/var/lib/roon-server/backup" diff --git a/hosts/spore/backup.nix b/hosts/spore/backup.nix index 381ff47b..f229ee94 100644 --- a/hosts/spore/backup.nix +++ b/hosts/spore/backup.nix @@ -1,8 +1,6 @@ -{config, ...}: { +{_}: { rc.backup = { enable = true; - paths = [ - config.services.postgresqlBackup.location - ]; + paths = []; }; } diff --git a/hosts/spore/services/db.nix b/hosts/spore/services/db.nix index ab8d59e8..87e28f9c 100644 --- a/hosts/spore/services/db.nix +++ b/hosts/spore/services/db.nix @@ -1,22 +1,5 @@ { - config, - pkgs, - ... -}: { - services.postgresql = { - enable = true; - package = pkgs.postgresql_16; - enableTCPIP = true; - authentication = pkgs.lib.mkOverride 10 '' - # Any user can connect to any database via Unix socket, local loopback, - # or Tailscale - local all all trust - host all all 127.0.0.1/32 trust - host all all 100.64.0.0/10 trust - ''; - settings = { - port = 5432; - max_connections = 150; - }; - }; + # PostgreSQL has been moved to glyph. Services on spore connect + # via Tailscale (glyph.rove-duck.ts.net:5432). + services.postgresql.enable = false; } diff --git a/hosts/spore/services/mastodon.nix b/hosts/spore/services/mastodon.nix index b407ed20..b65f61d7 100644 --- a/hosts/spore/services/mastodon.nix +++ b/hosts/spore/services/mastodon.nix @@ -48,7 +48,7 @@ in { configureNginx = true; database = { createLocally = false; - host = "127.0.0.1"; + host = "glyph.rove-duck.ts.net"; port = 5432; user = "mastodon"; passwordFile = "/dev/null"; # Not needed @@ -84,19 +84,7 @@ in { ]; }; - services.postgresql = lib.mkIf enable { - ensureUsers = [ - { - name = "mastodon"; - ensureDBOwnership = true; - } - ]; - ensureDatabases = ["mastodon"]; - }; - services.postgresqlBackup = { - #inherit enable; - databases = ["mastodon"]; - }; + # Database managed on glyph when re-enabled services.redis.servers.mastodon = { inherit enable; port = 31637; diff --git a/hosts/spore/services/web/auth.nix b/hosts/spore/services/web/auth.nix index 51b9a81c..db83b0df 100644 --- a/hosts/spore/services/web/auth.nix +++ b/hosts/spore/services/web/auth.nix @@ -22,6 +22,8 @@ host = "id.zx.dev"; useACMEHost = "zx.dev"; encryptionKeyFile = config.age.secrets.pocket-id-encryption-key.path; + databaseURL = "postgres://pocketid@glyph.rove-duck.ts.net/pocketid"; + localDatabase = false; }; authProxy = { host = "oauth.zx.dev"; diff --git a/modules/nixos/web/auth.nix b/modules/nixos/web/auth.nix index d51fb437..213deca4 100644 --- a/modules/nixos/web/auth.nix +++ b/modules/nixos/web/auth.nix @@ -30,6 +30,25 @@ in { example = "id.example.org"; }; + databaseURL = lib.mkOption { + type = lib.types.str; + default = "postgres://pocketid@/pocketid?host=/run/postgresql"; + description = '' + PostgreSQL connection string for pocket-id. + ''; + example = "postgres://pocketid@glyph.rove-duck.ts.net/pocketid"; + }; + + localDatabase = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether the database is managed locally. When true, PostgreSQL + is configured with the pocketid database and user, and pocket-id + depends on postgresql.service. + ''; + }; + encryptionKeyFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; @@ -122,29 +141,17 @@ in { settings = { APP_URL = "https://${cfg.issuer.host}"; TRUST_PROXY = true; - DB_CONNECTION_STRING = "postgres://pocketid@/pocketid?host=/run/postgresql"; + DB_CONNECTION_STRING = cfg.issuer.databaseURL; ENCRYPTION_KEY_FILE = cfg.issuer.encryptionKeyFile; }; }; - services.postgresql = { - ensureDatabases = ["pocketid"]; - ensureUsers = [ - { - name = "pocketid"; - ensureDBOwnership = true; - } - ]; - }; - services.postgresqlBackup = { - enable = lib.mkDefault true; - databases = ["pocketid"]; - }; - systemd.services.pocket-id = { wants = ["network-online.target"]; - after = ["postgresql.service" "network-online.target"]; - requires = ["postgresql.service"]; + after = + ["network-online.target"] + ++ lib.optionals cfg.issuer.localDatabase ["postgresql.service"]; + requires = lib.optionals cfg.issuer.localDatabase ["postgresql.service"]; }; services.oauth2-proxy = { @@ -205,6 +212,21 @@ in { }; }; }) + (mkIf (cfg.enable && cfg.issuer.localDatabase) { + services.postgresql = { + ensureDatabases = ["pocketid"]; + ensureUsers = [ + { + name = "pocketid"; + ensureDBOwnership = true; + } + ]; + }; + services.postgresqlBackup = { + enable = lib.mkDefault true; + databases = ["pocketid"]; + }; + }) (let vhosts = config.services.nginx.virtualHosts; vhostsRequiringAuth = mapNames (lib.filter (set: set.value.requireAuth) (lib.attrsToList vhosts)); From 6ebd8989fdbfe5456241fd40b3e7cd31934b620a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 21:15:48 -0700 Subject: [PATCH 2/6] fix: remove owner/group from attic-credentials secret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit atticd uses DynamicUser so the atticd user doesn't exist during activation when agenix decrypts secrets. Default to root:root — systemd reads the EnvironmentFile as root before dropping privileges. Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/attic.nix | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hosts/glyph/services/attic.nix b/hosts/glyph/services/attic.nix index b855e189..6f4c878f 100644 --- a/hosts/glyph/services/attic.nix +++ b/hosts/glyph/services/attic.nix @@ -1,10 +1,5 @@ {config, ...}: { - age.secrets.attic-credentials = { - file = ./../secrets/attic-credentials.age; - mode = "440"; - owner = "atticd"; - group = "atticd"; - }; + age.secrets.attic-credentials.file = ./../secrets/attic-credentials.age; systemd.services.atticd = { after = ["postgresql.service"]; From 215706e916375eb22c4c5d2d6756d74f78fd1fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 21:20:29 -0700 Subject: [PATCH 3/6] fix: use lib in spore backup module to satisfy statix and NixOS args Co-Authored-By: Claude Opus 4.6 --- hosts/spore/backup.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosts/spore/backup.nix b/hosts/spore/backup.nix index f229ee94..4d32d5f6 100644 --- a/hosts/spore/backup.nix +++ b/hosts/spore/backup.nix @@ -1,6 +1,6 @@ -{_}: { +{lib, ...}: { rc.backup = { - enable = true; + enable = lib.mkForce true; paths = []; }; } From 00c4bfee6877a5ff06fe5b7d13e9e84b14eccd1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 21:30:55 -0700 Subject: [PATCH 4/6] fix: update Attic cache public key after PostgreSQL migration The signing keypair was regenerated when the cache was recreated on the new PostgreSQL backend. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci.yml | 2 +- flake.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4f48fbe..7043af25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: github_access_token: ${{ secrets.GITHUB_TOKEN }} extra_nix_config: | extra-substituters = https://cache.zx.dev/main - extra-trusted-public-keys = main:sbkS1Xz6P4g66iyttRGj/o8aPODE6bVG9oKT98/ULKI= + extra-trusted-public-keys = main:mu0jkxdJTGWC3djDSEQb3rvZgqlhA8WVMulcTo5IW6c= - name: Configure Attic cache run: | nix profile install --inputs-from . attic#attic-client diff --git a/flake.nix b/flake.nix index c324045d..72d01cf6 100644 --- a/flake.nix +++ b/flake.nix @@ -122,7 +122,7 @@ "https://cache.zx.dev/main" ]; extra-trusted-public-keys = [ - "main:sbkS1Xz6P4g66iyttRGj/o8aPODE6bVG9oKT98/ULKI=" + "main:mu0jkxdJTGWC3djDSEQb3rvZgqlhA8WVMulcTo5IW6c=" ]; }; }; From 3c70f249b679c0fce3441f87345cdbe17c79a906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 21:55:48 -0700 Subject: [PATCH 5/6] refactor: remove redundant postgresql dependency from atticd The atticd NixOS module already detects local PostgreSQL from the database URL (checks for /run/postgresql) and adds the systemd after/requires dependencies automatically. Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/attic.nix | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hosts/glyph/services/attic.nix b/hosts/glyph/services/attic.nix index 6f4c878f..946937c7 100644 --- a/hosts/glyph/services/attic.nix +++ b/hosts/glyph/services/attic.nix @@ -1,11 +1,6 @@ {config, ...}: { age.secrets.attic-credentials.file = ./../secrets/attic-credentials.age; - systemd.services.atticd = { - after = ["postgresql.service"]; - requires = ["postgresql.service"]; - }; - services.atticd = { enable = true; environmentFile = config.age.secrets.attic-credentials.path; From c9e13cfbaec2dba57254ff702ddb6bfb3c5bd5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 21:58:59 -0700 Subject: [PATCH 6/6] refactor: remove unused db.nix and backup.nix from spore PostgreSQL and database backups have moved to glyph. These files were left as stubs and are no longer needed. Co-Authored-By: Claude Opus 4.6 --- hosts/spore/backup.nix | 6 ------ hosts/spore/default.nix | 1 - hosts/spore/services/db.nix | 5 ----- hosts/spore/services/default.nix | 1 - 4 files changed, 13 deletions(-) delete mode 100644 hosts/spore/backup.nix delete mode 100644 hosts/spore/services/db.nix diff --git a/hosts/spore/backup.nix b/hosts/spore/backup.nix deleted file mode 100644 index 4d32d5f6..00000000 --- a/hosts/spore/backup.nix +++ /dev/null @@ -1,6 +0,0 @@ -{lib, ...}: { - rc.backup = { - enable = lib.mkForce true; - paths = []; - }; -} diff --git a/hosts/spore/default.nix b/hosts/spore/default.nix index 753e9fcc..e18f6537 100644 --- a/hosts/spore/default.nix +++ b/hosts/spore/default.nix @@ -9,7 +9,6 @@ (modulesPath + "/installer/scan/not-detected.nix") (modulesPath + "/profiles/qemu-guest.nix") ./disk-config.nix - ./backup.nix ./services ]; diff --git a/hosts/spore/services/db.nix b/hosts/spore/services/db.nix deleted file mode 100644 index 87e28f9c..00000000 --- a/hosts/spore/services/db.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ - # PostgreSQL has been moved to glyph. Services on spore connect - # via Tailscale (glyph.rove-duck.ts.net:5432). - services.postgresql.enable = false; -} diff --git a/hosts/spore/services/default.nix b/hosts/spore/services/default.nix index 10e7554e..114b2c36 100644 --- a/hosts/spore/services/default.nix +++ b/hosts/spore/services/default.nix @@ -4,7 +4,6 @@ ... }: { imports = [ - ./db.nix ./grafana.nix ./homepage-dashboard.nix ./mastodon.nix