Overview
PostgreSQL is a widely used open-source relational database. This template deploys one PostgreSQL 18 server on a persistent volume, an optional PgBouncer connection pooler in front of it, and an optional cron job that writes compressedpg_dumpall backups to AWS S3, Google Cloud Storage or a MinIO (S3-compatible) bucket.
Database credentials are not template values. The server reads its username, password and database name from a dictionary secret you create before installing, so no password passes through Helm or lands in the release. The template creates no secret of its own.
What Gets Created
Prerequisites
One secret must exist before you install. It holds the credentials your applications put in their connection strings. Secrets are org-level, so no GVC flag is involved.Create the database credentials secret
username, password and database. PostgreSQL creates that user (as a superuser) and that database the first time the volume is initialized:config.credentialsSecretName to this name. Read it back later with cpln secret reveal SECRET_NAME -o yaml — a bare cpln secret reveal prints only a summary table, not the values.Installation
Install the chart from the marketplace registry, pointing it at the secret you created:UI
CLI
Terraform
Pulumi
Configuration
Each block below is the shipped default for that part ofvalues.yaml.
Server and Resources
maxCpu to minCpu may not exceed 4:1. Raising maxCpu without raising minCpu can cross that limit and is rejected when the workload is applied.Credentials
Storage
Internal Access
internalAccess can take several minutes to take effect — re-test before concluding it did not apply.
PgBouncer
PgBouncer multiplexes many application connections into a small pool of real database connections. When enabled it becomes the endpoint your applications connect to, and it reads the same credentials secret and identity as the server — nothing extra to configure.Backup
A cron workload runspg_dumpall on the schedule and uploads a gzip-compressed SQL dump of every database and role to the bucket for the chosen provider. It authenticates with the username and password from your credentials secret. Each provider’s bucket, cloud account and policy steps are under Backing Up.
backup.image must match the server’s major version: tag 18.1.0 for postgres:18, 17.1.0 for postgres:17.Connecting
This template exposes no public endpoint. Everything below is reachable from inside the GVC, subject tointernalAccess.type.
psql:
psql inside the container, where the credentials are already present as environment variables:
Operations
Backing Up
Backups are off by default. Enable them withbackup.enabled: true, pick a backup.provider, and complete that provider’s setup below first. The job writes postgres-TIMESTAMP.sql.gz objects under backup.PROVIDER.prefix in your bucket on every run of backup.schedule.
Backup Prerequisites
Complete the steps for your provider before enabling backups.AWS S3
-
Create an S3 bucket. Set
backup.aws.bucketto its name andbackup.aws.regionto its region. -
If you do not have one yet, create a Control Plane cloud account for the AWS account holding the bucket. Set
backup.aws.cloudAccountNameto its name. -
Create an IAM policy scoped to exactly that bucket, replacing
YOUR_BUCKET_NAME:
- Set
backup.aws.policyNameto that policy’s name. The template attaches it to the workload identity and nothing else — the bucket in your policy is the only storage the backup job can reach.
GCS
-
Create a GCS bucket. Set
backup.gcp.bucketto its name. -
Create a Control Plane cloud account for the GCP project holding the bucket, and set
backup.gcp.cloudAccountNameto its name. -
Grant the cloud account’s service account the Storage Admin (
roles/storage.admin) role. The template additionally binds the identity toroles/storage.objectAdminon exactly the bucket inbackup.gcp.bucket.
MinIO
No cloud account is needed — the job authenticates with a second prerequisite secret.-
Create the bucket in MinIO. Set
backup.minio.bucketto its name. -
Set
backup.minio.endpointto the S3 API address including the port. For the minio template deployed in the same GVC that ishttp://WORKLOAD_NAME.GVC_NAME.cpln.local:9000. -
Create a dictionary secret holding exactly the keys
accessKeyandsecretKey, and setbackup.minio.credentialsSecretNameto its name. For theminiotemplate these are its admin username and password:
reveal on this secret as well as the database credentials secret, and on nothing else.
Restoring a Backup
Each backup is a gzip-compressedpg_dumpall script, so it is restored by connecting to the postgres maintenance database as the superuser from your credentials secret; the script recreates the roles and databases it contains. Run the commands from a client that can reach both the bucket and the server — a workload inside the GVC, or your own machine with cpln port-forward RELEASE_NAME-postgres 5432:5432 --gvc GVC_NAME open and --host=127.0.0.1 in place of the internal hostname.
- AWS S3
- GCS
- MinIO
already exists errors for the role and database that the server created at first boot are expected; the rest of the script loads normally. Restore into an empty database (a fresh install, or one whose databases you have dropped) to avoid duplicate-object errors on existing tables.
Rotating the Password
The server reads the secret only when the volume is first initialized, so change the password inside PostgreSQL first, then update the secret to match:Upgrading From Earlier Versions
Template versions up to 3.3.0 took the database credentials as plain Helm values with a working default password, and wrote them into a chart-owned secret named after the release. Version 3.4.0 removed both, and an upgrade that still carries the old keys is refused at render — your existing release is left running rather than restarted against a different password.Read the credentials the database already uses
Create the prerequisite secret with those same values
username, password and database. Different values here do not change the database — they only leave the workload unable to authenticate.Remove the old keys from your values
config.username, config.password and config.database, and set config.credentialsSecretName to your secret’s name. If you back up to MinIO, replace backup.minio.accessKey and backup.minio.secretKey with backup.minio.credentialsSecretName.Upgrade, then rotate any default password
Scaling and Availability
- The server is pinned to one replica. Do not raise it — a single volume set backs a single server, and a second replica cannot mount it. Choose PostgreSQL Highly Available or Postgres Multi Location when you need failover.
- PgBouncer is stateless;
pgbouncer.replicasscales it horizontally for high connection counts. - Every
cpln helm upgraderestarts the server, including the first upgrade after an install even when nothing changed. Plan each one as a short write outage. - A node failure reschedules the server and reattaches the same volume. The exposure is minutes of downtime, not data loss. The volume set is not snapshotted on a schedule — it takes one final snapshot when it is deleted, kept for seven days — so use the backup job for point-in-time copies.
Troubleshooting
Workload never becomes ready and cpln logs returns nothing
Workload never becomes ready and cpln logs returns nothing
cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-postgres"}' --limit 50 --since 10m returns zero lines.Cause: config.credentialsSecretName names a secret that does not exist, so the container is never started. The message is only visible in status.versions[].message:cpln workload force-redeployment RELEASE_NAME-postgres --gvc GVC_NAME.Upgrade fails with 'config.username was REMOVED in postgres 3.4.0'
Upgrade fails with 'config.username was REMOVED in postgres 3.4.0'
cpln helm upgrade stops before touching any resource:config.password, config.database, and backup.minio.accessKey / backup.minio.secretKey each produce the same kind of message under their own name.Cause: your values still carry keys that were removed in 3.4.0.Fix: follow Upgrading From Earlier Versions. Your running release is unaffected until the upgrade succeeds.Password changed in the secret but the old one still works
Password changed in the secret but the old one still works
password key in the credentials secret, but PostgreSQL still accepts the previous password and rejects the new one.Cause: the secret is applied only when the volume is first initialized. Afterwards the stored password lives in the database, not the secret.Fix: change it inside PostgreSQL and then update the secret to match — see Rotating the Password.PgBouncer admin console rejects the database user with 'FATAL: not allowed'
PgBouncer admin console rejects the database user with 'FATAL: not allowed'
psql -d pgbouncer against the pooler fails with FATAL: not allowed.Cause: the PgBouncer image restricts its admin console to the postgres user. This is upstream behaviour, not a template setting.Fix: connect to your application database through the pooler as normal; use the admin console only if your credentials secret’s username is postgres.Apply rejected with 'The ratio between cpu and minCpu must be less than 4:1'
Apply rejected with 'The ratio between cpu and minCpu must be less than 4:1'
resources.maxCpu is more than four times resources.minCpu, which the platform rejects on a stateful workload.Fix: raise resources.minCpu or lower resources.maxCpu so the ratio is at most 4:1.Important Notes
- Create the credentials secret before installing. A reference to a secret that does not exist wedges the workload with no log output; see Prerequisites for the one command that shows the reason.
- Credentials are read only when the volume is first initialized. Rotate with
ALTER ROLEinside PostgreSQL, then update the secret — see Rotating the Password. - Do not scale the server past one replica. It is a single instance on a single volume, not a replicated cluster.
cpln helm uninstalldeletes the volume set and the database with it. Your credentials secret is yours and is left alone.- Every
cpln helm upgraderestarts the server. Treat each one as a short planned write outage. - Upgrading from 3.3.0 or earlier is a breaking change — the removed keys stop the upgrade rather than resetting a password. See Upgrading From Earlier Versions.
External References
PostgreSQL Documentation
PostgreSQL Backup and Restore
pg_dumpall dumps and restores