Server Database — Managed PostgreSQL and Redis
Purpose
The database submodule provides a managed interface for PostgreSQL and Redis across the server infrastructure. It centralizes database configuration on the Database Coordinator (config.server.databasePrimaryHost) while allowing client services to declaratively request databases. It automates provisioning of PostgreSQL databases and roles, management of Redis database IDs via static mappings, synchronization of service lifecycle with database availability using the IO Guardian, and automated password handling via SOPS secrets.
Entry Point
The module is implemented across several files in modules/nixos/server/database/:
- Main file: default.nix: Core options and connection management.
- Supporting file: postgres.nix: PostgreSQL-specific provisioning and secrets.
- Supporting file: redis.nix: Redis-specific ID mappings and security.
- Supporting file: guardian.nix: Lifecycle synchronization and the IO Guardian.
Options
server.database.dependentServices
| Type | list of string |
| Default | [ ] |
List of systemd service names that depend on io databases. These services will be automatically bound to the io-databases.target and will stop/start when databases become unavailable/available.
server.database.host
| Type | string |
| Default | if isThisIOPrimaryHost then "localhost" else config.server.ioPrimaryHost |
The hostname or IP address to use when connecting to managed databases.
This is “localhost” when running on the host,
and
server.database.postgres
| Type | attribute set of (submodule) |
| Default | { } |
This option has no description.
server.database.postgres.<name>.database
| Type | string |
| Default | "‹name›" |
This option has no description.
server.database.postgres.<name>.host
| Type | string |
| Default | config.server.database.host |
This option has no description.
server.database.postgres.<name>.password
| Type | submodule |
| Default | { } |
This option has no description.
server.database.postgres.<name>.password.group
| Type | null or string |
| Default | null |
This option has no description.
server.database.postgres.<name>.password.owner
| Type | null or string |
| Default | null |
This option has no description.
server.database.postgres.<name>.password.path
| Type | absolute path |
| Default | config.sops.secrets."POSTGRES/${ toUpper config.server.database.postgres.${name}.database |> builtins.replaceStrings [ "-" ] [ "_" ] }_PASSWORD".path; |
This option has no description.
server.database.postgres.<name>.port
| Type | signed integer |
| Default | config.server.database.postgres.‹name›.port |
This option has no description.
server.database.postgres.<name>.user
| Type | string |
| Default | "‹name›" |
This option has no description.
server.database.redis
| Type | attribute set of (submodule) |
| Default | { } |
This option has no description.
server.database.redis.<name>.database_id
| Type | signed integer |
| Default | staticDbIdMappings.‹name› or (-1) |
This option has no description.
server.database.redis.<name>.host
| Type | string |
| Default | config.server.database.host |
This option has no description.
server.database.redis.<name>.port
| Type | signed integer |
| Default | (getIOPrimaryHostAttr "services.redis.servers")."".port |
This option has no description.
server.database.redis.<name>.prefix
| Type | string |
| Default | "‹name›" |
This option has no description.
server.database.postgres
| Type | attribute set of (submodule) |
| Default | { } |
This option has no description.
server.database.postgres.<name>.database
| Type | string |
| Default | "‹name›" |
This option has no description.
server.database.postgres.<name>.host
| Type | string |
| Default | config.server.database.host |
This option has no description.
server.database.postgres.<name>.password
| Type | submodule |
| Default | { } |
This option has no description.
server.database.postgres.<name>.password.group
| Type | null or string |
| Default | null |
This option has no description.
server.database.postgres.<name>.password.owner
| Type | null or string |
| Default | null |
This option has no description.
server.database.postgres.<name>.password.path
| Type | absolute path |
| Default | config.sops.secrets."POSTGRES/${ toUpper config.server.database.postgres.${name}.database |> builtins.replaceStrings [ "-" ] [ "_" ] }_PASSWORD".path; |
This option has no description.
server.database.postgres.<name>.port
| Type | signed integer |
| Default | config.server.database.postgres.‹name›.port |
This option has no description.
server.database.postgres.<name>.user
| Type | string |
| Default | "‹name›" |
This option has no description.
server.database.redis
| Type | attribute set of (submodule) |
| Default | { } |
This option has no description.
server.database.redis.<name>.database_id
| Type | signed integer |
| Default | staticDbIdMappings.‹name› or (-1) |
This option has no description.
server.database.redis.<name>.host
| Type | string |
| Default | config.server.database.host |
This option has no description.
server.database.redis.<name>.port
| Type | signed integer |
| Default | (getIOPrimaryHostAttr "services.redis.servers")."".port |
This option has no description.
server.database.redis.<name>.prefix
| Type | string |
| Default | "‹name›" |
This option has no description.
Architecture / Services / Scope
Connection Management
The server.database.host option determines how services connect to databases. On the Database Coordinator (config.server.databasePrimaryHost), it defaults to localhost. On all other hosts, it defaults to the value of config.server.databasePrimaryHost.
PostgreSQL Management
When a service defines a database in server.database.postgres:
- Automatic Provisioning: The Database Coordinator automatically creates the database and a role with the same name.
- Password Management: A SOPS secret is expected at
POSTGRES/<DB_NAME_UPPER>_PASSWORD. Database names containing hyphens (-) replace them with underscores (_) when constructing the secret path. The system automatically sets this password for the role during thepostgresql-setupservice. - Aggregated Configuration: The Database Coordinator collects all PostgreSQL requirements from across the entire flake to ensure all necessary extensions and initial scripts are loaded.
Redis Management
Redis management uses a similar aggregation pattern:
- Database IDs: Because Redis uses numeric IDs (0-15), the system uses a static mapping file (
redis-mappings.json) on the Database Coordinator to ensure consistent ID assignment across the fleet. - Password Management: A shared password for the primary Redis instance is managed via
REDIS/PASSWORDin SOPS. - Tooling: Use the
update-redis-mappingscommand on the Database Coordinator to update the mapping file when adding new Redis clients.
IO Guardian Coordination
Lifecycle management is handled by the IO Guardian. A pre-shared key for guardian communication is managed via the DB_GUARDIAN_PSK SOPS secret.
- On Clients: Services that use these database modules are automatically bound to
io-databases.target. This ensures they only start when the remote databases are reachable and stop before the databases go offline. Reachability and readiness checks are handled by the sharedwait-for-io-toolspackage (wait-for-ioandwait-for-io-databases). - On Database Coordinator: The
io-database-coordinatorservice manages thedrainandundrainsignals sent to clients during system startup and shutdown.
Secrets
POSTGRES/<DB_NAME_UPPER>_PASSWORD: Per-database role password, provisioned duringpostgresql-setup.REDIS/PASSWORD: Shared password for the primary Redis instance.DB_GUARDIAN_PSK: Pre-shared key for IO Guardian communication.
Operational Notes / Assumptions
- The host designated as the Database Coordinator (
config.server.databasePrimaryHost) is responsible for running the actual database engines. It aggregates all database requirements from every host in the flake and applies them locally. - Client services request databases by declaring entries in
server.database.postgresorserver.database.redis; the submodule wires up connection info, secrets, and lifecycle binding.