Database Configuration#
User Info Service uses a PostgreSQL database as the shared store that carries directory data from the writer to the readers. The writer keeps this store aligned with the identity provider; readers load it on startup and refresh it periodically. This page describes what the store holds, how to point the service at it, how to secure the connection, how to supply static or rotating credentials, and how to size the connection budget when running many readers.
The store is required for the recommended synchronized-directory deployment. It is not required for the reader-only live mode, in which every request is answered directly from the identity provider and no database is used. See User Info Service Configuration for how to choose between the two deployments.
Connection Settings#
Point the service at the database with the following settings. Each has an environment
variable and a corresponding Helm value under providers.cache.postgres.
Setting |
Environment variable |
Helm value ( |
Default |
Description |
|---|---|---|---|---|
Enabled |
|
|
|
Use the shared store. Disable only for the reader-only live mode. |
Host |
|
|
|
Database server hostname. |
Port |
|
|
|
Database server port. |
Database name |
|
|
|
Name of the database used for directory storage. |
User |
|
|
|
Database user the service connects as. |
Connection budget |
|
|
|
Maximum number of simultaneous connections each instance opens. See Connection Budget and Scaling. |
Transport security |
|
|
|
TLS requirement for the connection. See Transport Security. |
The writer requires the store to be enabled and will refuse to start otherwise. A reader requires the store whenever caching is enabled; when caching is disabled it runs in live mode and ignores these settings.
The database user needs permission to read and write the service’s data and to create the structures it uses on first startup. Any PostgreSQL-compatible database can be used. The deployment guide recommends Azure Database for PostgreSQL for production and shows an in-cluster CloudNativePG cluster as an alternative.
Note
The defaults in the table above are the service’s built-in defaults. The Helm chart sets its
own values in values.yaml — notably an empty host, which you must supply, and
sslMode: disable — so in a chart deployment the effective default comes from the chart, not
from this table.
Transport Security#
The connection to the database can be encrypted with TLS. The transport-security setting has three values:
Value |
Behavior |
|---|---|
|
Never use TLS. The connection is sent in plaintext. |
|
Try TLS first, and fall back to plaintext if the server does not accept it. |
|
Require TLS. Refuse to connect if the server does not support it. |
When TLS is negotiated, the database server’s certificate is verified against the trusted root
certificates that ship in the service container. Use require when the database is reached
over an untrusted network. The deployment examples use disable because they connect to a
database on a trusted in-cluster network.
Credentials#
The service authenticates to the database with a password. The password can be supplied in two ways: a static password, or a short-lived token that is rotated by an external provider. The username is always taken from the connection settings above.
Static Password#
Provide the password directly through the POSTGRES_PASSWORD environment variable. In Helm,
reference an existing Kubernetes Secret rather than placing the password in the values file:
providers:
cache:
postgres:
password:
secretRef:
name: "microsoft-graph-userinfo-service-pg-credentials"
key: "password"
This is the simplest option and is appropriate when the password does not change during the lifetime of the deployment.
Rotating Token#
For deployments where the database password is short-lived and refreshed periodically, the service can read the password from a token file instead of a static value. When a token file is configured, it takes precedence over the static password.
The token file is a small document containing the current credential and the moment it expires. An external token provider — typically a sidecar running alongside the service — keeps the file up to date. The service watches the file and, when the credential changes, transparently begins using the new one for future connections. Connections already open continue to work, because the database only checks the credential when a connection is first made.
Configure the rotating token in Helm:
providers:
cache:
postgres:
tokenFile:
enabled: true
path: "/etc/secrets/postgres-token/token"
pollInterval: "15s"
waitTimeout: "30s"
waitInterval: "1s"
provisionerSidecar:
container: {} # the token-provider container to run alongside the service
The settings that control this behavior:
Setting |
Environment variable |
Helm value ( |
Default |
Description |
|---|---|---|---|---|
Token file |
|
|
— |
Location of the token file inside the container. Enabling |
Rotation check interval |
|
|
|
How often the file is checked for a rotated credential. |
Startup wait timeout |
|
|
|
How long to wait at startup for the file to appear before giving up. |
Startup wait interval |
|
|
|
How often to re-check for the file while waiting at startup. |
Because the token provider may mount the file a few seconds after the service starts, a missing or not-yet-written file is retried during the startup wait window rather than treated as an immediate failure. A credential that is empty or already expired is rejected. The token file uses the same JSON format as the service-identity access-token file.
Recommended: Azure Database for PostgreSQL Flexible Server#
For production, run the shared store on Azure Database for PostgreSQL flexible server, Microsoft’s fully managed PostgreSQL service. It provides automated backups, high availability, and scaling without the operational burden of running the database yourself, and it keeps the directory data in the same cloud as the identity provider the service already reads from.
This managed option pairs naturally with the service’s existing Microsoft Entra ID integration. The same identity system used to read the directory can also authenticate the database connection, so no separate database password has to be created, stored, or rotated by hand:
Authenticate with Microsoft Entra ID. Enable Microsoft Entra authentication on the server and grant access to the identity the service runs as. The service then connects with a short-lived Microsoft Entra access token used in place of a password. Set the database user to that Entra identity. See Microsoft Entra authentication in Azure Database for PostgreSQL and Use Microsoft Entra ID authentication.
Supply the token through the rotating-token credential. A Microsoft Entra access token is valid only for a short period, on the order of an hour, and must be refreshed before it expires. Configure the rotating token credential and run a token provider alongside the service that obtains an Entra token and keeps the token file current. The service picks up each refreshed token automatically and existing connections keep working.
Require an encrypted connection. Azure Database for PostgreSQL enforces encrypted connections and rejects unencrypted ones, so set the transport security setting to
require. The service verifies the server’s certificate against the trusted root certificates in its container, which include the authorities Azure uses. See Transport Layer Security in Azure Database for PostgreSQL.
Static-password authentication against the managed server also works and is a valid starting point, but Entra-based authentication with the rotating-token credential is preferred because it avoids a long-lived database password entirely.
Connection Budget and Scaling#
Each service instance — the writer and every reader — opens up to its configured connection
budget (connections, default 4) against the database. The database must allow at least the
sum of the budgets of all running instances, plus headroom for administrative access.
As a starting point, size the database’s connection limit for:
(number of readers + 1 writer) × connection budget per instance + headroom
For example, with the default budget of 4 and one writer plus three readers, the service can
use up to sixteen connections. When scaling readers out for more capacity, or when raising the
per-instance budget for higher throughput, raise the database’s connection limit to match.
Two points to keep in mind when sizing:
Writer full synchronization. While the writer is performing a full synchronization it holds one of its connections aside to coordinate with any other writer, so that only one full synchronization runs at a time. Keep the writer’s budget at two or more so it retains a working connection during a full synchronization.
Readers are read-only. Readers reload the stored directory on their refresh interval and otherwise leave their connections idle, so their steady-state load on the database is light. Most connection pressure comes from adding more reader replicas rather than from request volume.
The number of connections currently in use by each instance is reported as a metric; see Observability for how to monitor it.