feat: refactored database configuration

This commit is contained in:
Leyla Becker 2025-10-27 03:55:09 -05:00
parent e57c1df6e5
commit f9c27c82b6
8 changed files with 229 additions and 204 deletions

View file

@ -102,13 +102,6 @@
directories = ["leyla_documents" "eve_documents" "users_documents" "media"];
};
};
postgres = {
extraUsers = {
leyla = {
isAdmin = true;
};
};
};
};
systemd.network = {
@ -220,6 +213,12 @@
};
services = {
# PostgreSQL database server
postgresql = {
enable = true;
adminUsers = ["leyla"];
};
# temp enable desktop environment for setup
# Enable the X11 windowing system.
xserver.enable = true;
@ -327,7 +326,7 @@
enable = true;
domain = "home.jan-leila.com";
openFirewall = true;
database = "postgres";
postgres.enable = true;
extensions = {
sonos.enable = true;

View file

@ -2,40 +2,31 @@
lib,
config,
...
}: {
config = lib.mkIf config.services.forgejo.enable (
lib.mkMerge [
{
host = {
postgres = {
enable = true;
};
};
}: let
usingPostgres = config.services.forgejo.database.type == "postgres";
in {
config = lib.mkIf config.services.forgejo.enable {
assertions = [
{
assertion = config.services.forgejo.settings.database.DB_TYPE == "postgres";
message = "Forgejo database type must be postgres";
assertion = !usingPostgres || config.services.postgresql.enable;
message = "PostgreSQL must be enabled when Forgejo database type is postgres";
}
{
assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (db: db == "forgejo") config.services.postgresql.ensureDatabases);
message = "Forgejo built-in database creation failed - expected 'forgejo' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}";
}
{
assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (user: user.name == "forgejo") config.services.postgresql.ensureUsers);
message = "Forgejo built-in user creation failed - expected user 'forgejo' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}";
}
];
}
(lib.mkIf config.host.postgres.enable {
host = {
postgres = {
extraUsers = {
forgejo = {
isClient = true;
createUser = true;
services.forgejo.database.createDatabase = lib.mkDefault usingPostgres;
systemd.services.forgejo = lib.mkIf usingPostgres {
requires = [
config.systemd.services.postgresql.name
];
};
};
extraDatabases = {
forgejo = {
name = "forgejo";
};
};
};
};
})
]
);
}

View file

@ -2,55 +2,52 @@
lib,
config,
...
}: let
dbUser = "hass";
in {
config = lib.mkIf config.services.home-assistant.enable (
lib.mkMerge [
{
host = {
}: {
options.services.home-assistant = {
postgres = {
enable = true;
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Use PostgreSQL instead of SQLite";
};
user = lib.mkOption {
type = lib.types.str;
default = "hass";
description = "Database user name";
};
database = lib.mkOption {
type = lib.types.str;
default = "hass";
description = "Database name";
};
};
};
config = lib.mkIf config.services.home-assistant.enable {
assertions = [
{
assertion = config.services.home-assistant.database == "postgres";
message = "Home Assistant database type must be postgres";
assertion = !config.services.home-assistant.postgres.enable || config.services.postgresql.enable;
message = "PostgreSQL must be enabled when using postgres database for Home Assistant";
}
];
}
(lib.mkIf config.host.postgres.enable {
host = {
postgres = {
extraUsers = {
${dbUser} = {
isClient = true;
createUser = true;
};
};
extraDatabases = {
${dbUser} = {
name = dbUser;
};
};
};
services.postgresql.databases.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable {
enable = true;
user = config.services.home-assistant.postgres.user;
database = config.services.home-assistant.postgres.database;
};
services.home-assistant = {
services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable {
extraPackages = python3Packages:
with python3Packages; [
psycopg2
];
};
systemd.services.home-assistant = {
systemd.services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable {
requires = [
config.systemd.services.postgresql.name
];
};
})
]
);
};
}

View file

@ -3,24 +3,28 @@
config,
...
}: {
config = lib.mkIf config.services.immich.enable (lib.mkMerge [
config = lib.mkIf config.services.immich.enable {
assertions = [
{
host = {
postgres = {
enable = true;
};
};
assertion = !config.services.immich.database.enable || config.services.postgresql.enable;
message = "PostgreSQL must be enabled when using postgres database for Immich";
}
(lib.mkIf config.host.postgres.enable {
host = {
postgres = {
extraUsers = {
${config.services.immich.database.user} = {
isClient = true;
{
assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (db: db == "immich") config.services.postgresql.ensureDatabases);
message = "Immich built-in database creation failed - expected 'immich' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}";
}
{
assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (user: user.name == "immich") config.services.postgresql.ensureUsers);
message = "Immich built-in user creation failed - expected user 'immich' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}";
}
];
# Note: Immich has built-in database creation via services.immich.database.createDB we only add the systemd dependency
systemd.services.immich-server = lib.mkIf config.services.immich.database.enable {
requires = [
config.systemd.services.postgresql.name
];
};
};
};
};
})
]);
}

View file

@ -3,32 +3,46 @@
config,
...
}: {
config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [
options.services.panoramax = {
database = {
postgres = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Use PostgreSQL instead of SQLite";
};
user = lib.mkOption {
type = lib.types.str;
default = "panoramax";
description = "Database user name";
};
database = lib.mkOption {
type = lib.types.str;
default = "panoramax";
description = "Database name";
};
};
};
};
config = lib.mkIf config.services.panoramax.enable {
assertions = [
{
host = {
postgres = {
assertion = !config.services.panoramax.database.postgres.enable || config.services.postgresql.enable;
message = "PostgreSQL must be enabled when using postgres database for Panoramax";
}
];
services.postgresql.databases.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable {
enable = true;
user = config.services.panoramax.database.postgres.user;
database = config.services.panoramax.database.postgres.database;
};
systemd.services.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable {
requires = [
config.systemd.services.postgresql.name
];
};
};
}
(
lib.mkIf config.host.postgres.enable {
host = {
postgres = {
extraUsers = {
${config.services.panoramax.database.user} = {
isClient = true;
createUser = true;
};
};
extraDatabases = {
${config.services.panoramax.database.name} = {
name = config.services.panoramax.database.user;
};
};
};
};
}
)
]);
}

View file

@ -3,32 +3,28 @@
lib,
...
}: {
config = lib.mkIf config.services.paperless.enable (lib.mkMerge [
config = lib.mkIf config.services.paperless.enable {
assertions = [
{
host = {
postgres = {
enable = true;
};
};
assertion = !config.services.paperless.database.createLocally || config.services.postgresql.enable;
message = "PostgreSQL must be enabled when using local postgres database for Paperless";
}
(
lib.mkIf config.host.postgres.enable {
host = {
postgres = {
extraUsers = {
${config.services.paperless.database.user} = {
isClient = true;
createUser = true;
};
};
extraDatabases = {
${config.services.paperless.database.user} = {
name = config.services.paperless.database.user;
};
};
};
};
{
assertion = !config.services.paperless.database.createLocally || (builtins.any (db: db == "paperless") config.services.postgresql.ensureDatabases);
message = "Paperless built-in database creation failed - expected 'paperless' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}";
}
)
]);
{
assertion = !config.services.paperless.database.createLocally || (builtins.any (user: user.name == "paperless") config.services.postgresql.ensureUsers);
message = "Paperless built-in user creation failed - expected user 'paperless' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}";
}
];
services.paperless.database.createLocally = lib.mkDefault true;
systemd.services.paperless-scheduler = lib.mkIf config.services.paperless.database.createLocally {
requires = [
config.systemd.services.postgresql.name
];
};
};
}

View file

@ -5,7 +5,7 @@
}: let
dataDir = "/var/lib/postgresql/16";
in {
config = lib.mkIf (config.host.postgres.enable && config.host.impermanence.enable) {
config = lib.mkIf (config.services.postgresql.enable && config.host.impermanence.enable) {
assertions = [
{
assertion = config.services.postgresql.dataDir == dataDir;

View file

@ -4,70 +4,94 @@
pkgs,
...
}: let
adminUsers = lib.lists.filter (user: user.isAdmin) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers);
clientUsers = lib.lists.filter (user: user.isClient) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers);
createUsers = lib.lists.filter (user: user.createUser) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers);
createDatabases = lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraDatabases;
enabledDatabases = lib.filterAttrs (_: db: db.enable) config.services.postgresql.databases;
extraDatabasesList = config.services.postgresql.extraDatabases;
serviceDatabaseUsers = lib.mapAttrsToList (_: db: {
name = db.user;
ensureDBOwnership = true;
}) (lib.filterAttrs (_: db: db.ensureUser) enabledDatabases);
extraDatabaseUsers =
builtins.map (dbName: {
name = dbName;
ensureDBOwnership = true;
})
extraDatabasesList;
serviceDatabases = lib.mapAttrsToList (_: db: db.database) enabledDatabases;
extraDatabaseNames = extraDatabasesList;
serviceUserMappings = lib.mapAttrsToList (_: db: "user_map ${db.user} ${db.user}") enabledDatabases;
extraUserMappings = builtins.map (dbName: "user_map ${dbName} ${dbName}") extraDatabasesList;
builtinServiceMappings = let
forgejoMapping = lib.optional (config.services.forgejo.enable && config.services.forgejo.database.type == "postgres") "user_map forgejo forgejo";
immichMapping = lib.optional (config.services.immich.enable && config.services.immich.database.enable) "user_map immich immich";
paperlessMapping = lib.optional (config.services.paperless.enable && config.services.paperless.database.createLocally) "user_map paperless paperless";
in
forgejoMapping ++ immichMapping ++ paperlessMapping;
in {
options = {
host.postgres = {
enable = lib.mkEnableOption "enable postgres";
extraUsers = lib.mkOption {
services.postgresql = {
databases = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
name = lib.mkOption {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to create this database and user";
};
user = lib.mkOption {
type = lib.types.str;
default = name;
description = "Database user name";
};
isAdmin = lib.mkOption {
type = lib.types.bool;
default = false;
database = lib.mkOption {
type = lib.types.str;
default = name;
description = "Database name";
};
isClient = lib.mkOption {
ensureUser = lib.mkOption {
type = lib.types.bool;
default = false;
};
createUser = lib.mkOption {
type = lib.types.bool;
default = false;
default = true;
description = "Whether to ensure the user exists";
};
};
}));
default = {};
description = "Databases to create for services";
};
extraDatabases = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
type = lib.types.listOf lib.types.str;
default = [];
description = "Additional databases to create (user name will match database name)";
example = ["custom_db" "test_db"];
};
};
}));
default = {};
adminUsers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "System users who should have PostgreSQL superuser access";
example = ["leyla" "admin"];
};
};
};
config = lib.mkIf config.host.postgres.enable {
config = lib.mkIf config.services.postgresql.enable {
services = {
postgresql = {
enable = true;
package = pkgs.postgresql_16;
ensureUsers =
[
{
name = "postgres";
}
{name = "postgres";}
]
++ (
builtins.map (user: {
name = user.name;
ensureDBOwnership = true;
})
createUsers
);
ensureDatabases = builtins.map (database: database.name) createDatabases;
++ serviceDatabaseUsers ++ extraDatabaseUsers;
ensureDatabases = serviceDatabases ++ extraDatabaseNames;
identMap =
''
# ArbitraryMapName systemUser DBUser
@ -77,16 +101,16 @@ in {
superuser_map postgres postgres
''
+ (
lib.strings.concatLines (builtins.map (user: "superuser_map ${user.name} postgres") adminUsers)
lib.strings.concatLines (builtins.map (user: "superuser_map ${user} postgres") config.services.postgresql.adminUsers)
)
+ ''
# Client Users
''
+ (
lib.strings.concatLines (builtins.map (user: "user_map ${user.name} ${user.name}") clientUsers)
lib.strings.concatLines (serviceUserMappings ++ extraUserMappings ++ builtinServiceMappings)
);
# configuration here lets users access the db that matches their name and lets user postgres access everything
authentication = pkgs.lib.mkOverride 10 ''
# type database DBuser origin-address auth-method optional_ident_map
local all postgres peer map=superuser_map