Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

Typelist 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

Typestring
Defaultif 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 when connecting from other hosts.


server.database.postgres

Typeattribute set of (submodule)
Default{ }

This option has no description.


server.database.postgres.<name>.database

Typestring
Default"‹name›"

This option has no description.


server.database.postgres.<name>.host

Typestring
Defaultconfig.server.database.host

This option has no description.


server.database.postgres.<name>.password

Typesubmodule
Default{ }

This option has no description.


server.database.postgres.<name>.password.group

Typenull or string
Defaultnull

This option has no description.


server.database.postgres.<name>.password.owner

Typenull or string
Defaultnull

This option has no description.


server.database.postgres.<name>.password.path

Typeabsolute path
Defaultconfig.sops.secrets."POSTGRES/${ toUpper config.server.database.postgres.${name}.database |> builtins.replaceStrings [ "-" ] [ "_" ] }_PASSWORD".path;

This option has no description.


server.database.postgres.<name>.port

Typesigned integer
Defaultconfig.server.database.postgres.‹name›.port

This option has no description.


server.database.postgres.<name>.user

Typestring
Default"‹name›"

This option has no description.


server.database.redis

Typeattribute set of (submodule)
Default{ }

This option has no description.


server.database.redis.<name>.database_id

Typesigned integer
DefaultstaticDbIdMappings.‹name› or (-1)

This option has no description.


server.database.redis.<name>.host

Typestring
Defaultconfig.server.database.host

This option has no description.


server.database.redis.<name>.port

Typesigned integer
Default(getIOPrimaryHostAttr "services.redis.servers")."".port

This option has no description.


server.database.redis.<name>.prefix

Typestring
Default"‹name›"

This option has no description.


server.database.postgres

Typeattribute set of (submodule)
Default{ }

This option has no description.


server.database.postgres.<name>.database

Typestring
Default"‹name›"

This option has no description.


server.database.postgres.<name>.host

Typestring
Defaultconfig.server.database.host

This option has no description.


server.database.postgres.<name>.password

Typesubmodule
Default{ }

This option has no description.


server.database.postgres.<name>.password.group

Typenull or string
Defaultnull

This option has no description.


server.database.postgres.<name>.password.owner

Typenull or string
Defaultnull

This option has no description.


server.database.postgres.<name>.password.path

Typeabsolute path
Defaultconfig.sops.secrets."POSTGRES/${ toUpper config.server.database.postgres.${name}.database |> builtins.replaceStrings [ "-" ] [ "_" ] }_PASSWORD".path;

This option has no description.


server.database.postgres.<name>.port

Typesigned integer
Defaultconfig.server.database.postgres.‹name›.port

This option has no description.


server.database.postgres.<name>.user

Typestring
Default"‹name›"

This option has no description.


server.database.redis

Typeattribute set of (submodule)
Default{ }

This option has no description.


server.database.redis.<name>.database_id

Typesigned integer
DefaultstaticDbIdMappings.‹name› or (-1)

This option has no description.


server.database.redis.<name>.host

Typestring
Defaultconfig.server.database.host

This option has no description.


server.database.redis.<name>.port

Typesigned integer
Default(getIOPrimaryHostAttr "services.redis.servers")."".port

This option has no description.


server.database.redis.<name>.prefix

Typestring
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 the postgresql-setup service.
  • 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/PASSWORD in SOPS.
  • Tooling: Use the update-redis-mappings command 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 shared wait-for-io-tools package (wait-for-io and wait-for-io-databases).
  • On Database Coordinator: The io-database-coordinator service manages the drain and undrain signals sent to clients during system startup and shutdown.

Secrets

  • POSTGRES/<DB_NAME_UPPER>_PASSWORD: Per-database role password, provisioned during postgresql-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.postgres or server.database.redis; the submodule wires up connection info, secrets, and lifecycle binding.

References