Skip to content

External Database

By default, the Governance Platform Helm chart deploys a bundled PostgreSQL instance (PostgreSQL 17) inside the cluster with a persistent volume. This is convenient for evaluation and development, but production deployments should use a cloud-managed PostgreSQL service. A managed database provides automated backups, point-in-time recovery, high availability, and patching that the bundled instance does not.

Any PostgreSQL 17-compatible managed service works. Choose a provider and follow its setup guide:

The platform uses two databases on the same PostgreSQL instance:

  • governance - used by the Auth Service and Governance Service
  • IntegrityServiceDB - used by the Integrity Service

The bundled instance creates both automatically at startup. On an external instance, you create them yourself before deploying the chart:

CREATE DATABASE governance;
GRANT ALL PRIVILEGES ON DATABASE governance TO postgres;
CREATE DATABASE "IntegrityServiceDB";
GRANT ALL PRIVILEGES ON DATABASE "IntegrityServiceDB" TO postgres;

Database schema migrations still run automatically when the services start.

The chart configuration is the same for every provider - only the connection details differ. Disable the bundled PostgreSQL subchart and point the platform at the external instance in values.yaml:

values.yaml
# Disable the bundled PostgreSQL subchart
postgresql:
enabled: false
global:
postgresql:
host: "<instance-endpoint>" # From the provider setup guide
port: 5432
database: "governance"
username: "postgres" # Master username chosen during provisioning
# Managed PostgreSQL services all support TLS. Use "verify-full" with the
# provider CA bundle mounted; use "require" if you cannot provide a CA
# bundle but still want encryption in transit.
sslMode: "verify-full"
sslRootCert:
secretName: "postgres-ca" # Created in the provider setup guide
key: "ca.crt"

The database password continues to come from the platform-database secret defined in secrets.yaml - set it to the master password chosen during provisioning:

secrets.yaml
global:
secrets:
create: true
database:
secretName: "platform-database"
values:
username: "postgres"
password: "<master-password>" # Chosen during provisioning

Each service also supports per-service overrides under <service>.externalDatabase (host, port, name, user, sslMode, password secret reference) for advanced setups such as separate instances per service. Most deployments only need the global.postgresql settings above.

Detailed setup guides for these providers are not yet available, but the chart configuration above works unchanged - only the connection details differ:

Azure Database for PostgreSQL - Flexible Server

  • host: <server>.postgres.database.azure.com
  • username: plain <username> (not <username>@<server> - that format is only for the retired Single Server offering)
  • CA bundle: DigiCert Global Root CA (see the Azure documentation for the current certificate)
  • Network: private DNS zone with VNet integration, or VNet injection

GCP Cloud SQL for PostgreSQL

  • host: the instance private IP, or the Cloud SQL Auth Proxy sidecar address
  • CA bundle: download the server CA from the instance Connections tab
  • Network: private IP with VPC peering, or run the Cloud SQL Auth Proxy as a sidecar

Verify connectivity from inside the cluster before deploying the chart:

Terminal window
kubectl run psql-test --rm -it --restart=Never \
--namespace governance \
--image=postgres:17 -- \
psql "host=<instance-endpoint> port=5432 user=postgres sslmode=require" \
-c "SELECT datname FROM pg_database;"

The output must list both governance and IntegrityServiceDB. After deploying the chart, confirm the services started and ran their migrations:

Terminal window
kubectl logs deployment/governance-platform-auth-service \
--namespace governance | grep -i migration
  • Keep the instance private - no public accessibility; restrict network access to the cluster nodes
  • Use sslMode: "verify-full" with the provider CA bundle to prevent man-in-the-middle attacks
  • Enable storage encryption at rest when creating the instance
  • Store the master password only in secrets.yaml or an approved secret manager - never in values.yaml
  • Rotate the master password periodically and update the platform-database secret in the same change window
  • Enable deletion protection on the production instance