---
title: "External Database"
description: "Configure an external PostgreSQL database for deployment"
source: "Guardian Platform"
---
## Overview

By default, the Guardian 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:

### Supported Database Providers

- [AWS RDS for PostgreSQL](./aws-rds.mdx) (or Aurora PostgreSQL) - Amazon's managed relational database service
- **Azure Database for PostgreSQL - Flexible Server** - see [provider notes](#other-managed-postgresql-providers) below
- **GCP Cloud SQL for PostgreSQL** - see [provider notes](#other-managed-postgresql-providers) below

## Platform Databases

The platform uses these databases on the same PostgreSQL instance:

- `governance` - used by the Auth Service and Governance Service
- `IntegrityServiceDB` - used by the Integrity Service
- `guardian_gateway` - used by the gateway stack, only when `gateway-stack.enabled` is `true`

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

```sql
CREATE DATABASE governance;
GRANT ALL PRIVILEGES ON DATABASE governance TO postgres;

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

-- Only when the gateway stack is enabled
CREATE DATABASE guardian_gateway;
GRANT ALL PRIVILEGES ON DATABASE guardian_gateway TO postgres;
```

Database schema migrations still run automatically when the services start.

## Helm Configuration

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`:

```yaml title="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:

```yaml title="secrets.yaml"
global:
  secrets:
    create: true
    database:
      secretName: "platform-database"
      values:
        username: "postgres" # Required by the chart; services read the username from global.postgresql.username
        password: "<master-password>" # Chosen during provisioning
        # Only when gateway-stack.enabled is true; URL-encode a password with reserved characters.
        # The gateway chart cannot mount the CA bundle yet, so use "require": encrypted, but the
        # server certificate is not verified
        gatewayDsn: "postgres://postgres:<url-encoded-password>@<instance-endpoint>:5432/guardian_gateway?sslmode=require"
```

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.

## Other Managed PostgreSQL Providers

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: the root certificates Azure lists for Flexible Server TLS, combined into one `ca.crt`
- Extensions: allow-list `UUID-OSSP` before deploying, since the service migrations create it: `az postgres flexible-server parameter set --resource-group <resource-group> --server-name <server> --name azure.extensions --value UUID-OSSP`
- Network: private DNS zone with VNet integration, or VNet injection

**GCP Cloud SQL for PostgreSQL**

- `host`: the instance private IP
- CA bundle: download the server CA from the instance **Connections** tab
- Network: private IP with VPC peering. The Cloud SQL Auth Proxy sidecar pattern isn't supported, because the Governance Service and Integrity Service charts can't add sidecar containers

## Verification

Verify connectivity from inside the cluster before deploying the chart:

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

The output must list `governance` and `IntegrityServiceDB`, plus `guardian_gateway` when the gateway stack is enabled. After deploying the chart, confirm the services started and ran their migrations by checking the Auth Service log for `Database migrations completed successfully` or `No migrations to run - database is up to date`:

```bash
kubectl logs deployment/guardian-platform-auth-service \
  --namespace guardian
```

## Security Best Practices

- 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

## Official Documentation

- [Helm Chart Examples](https://github.com/eqtylab/deployment/tree/main/charts/guardian-platform/examples)

## Related

- [AWS RDS](./aws-rds.mdx) — step-by-step provisioning for RDS or Aurora PostgreSQL
- [Backup and Restore](./backup-and-restore.mdx#postgresql) — backup and snapshot guidance for managed and bundled PostgreSQL
- [Chart Values](./chart-values.mdx) — the full values and secrets files these settings go into
- [Helm Deployment](./helm-deployment.mdx) — deploying the platform against the external instance