Skip to main content

Overview

PgDog is a high-performance PostgreSQL connection pooler, load balancer, and query router written in Rust. It sits transparently in front of one or more PostgreSQL instances and appears to clients as a standard PostgreSQL server — no application code changes required, only a connection string update. PgDog parses SQL queries to automatically route writes (INSERT, UPDATE, DELETE, DDL) to a primary backend and distribute SELECT queries across replicas. It works with any PostgreSQL-compatible backend, including the Control Plane PostgreSQL and PostgreSQL Highly Available templates, or any external PostgreSQL endpoint. Every credential comes from a secret you create before installing. Nothing sensitive passes through Helm values, so no password lands in the release, the rendered manifest, the stored workload spec, or the logs.
Upgrading an install created with 1.0.0 is a breaking change. users[].name, users[].password and admin.password no longer exist, and an upgrade that still sets any of them stops with an error naming its replacement rather than silently falling back to a default password. See Upgrading From 1.0.0.

Architecture

  • PgDog workload — Stateless proxy that multiplexes client connections into a smaller pool of real backend connections, routes writes to a primary, and distributes reads across replicas. Listens on port 6432.
  • Config secret — The static half of pgdog.toml: general settings and the [[databases]] backends, rendered by the chart and mounted read-only. It holds no credentials.
  • Startup script secret — PgDog reads credentials only from files on disk and has no environment-variable interpolation, so a startup script assembles the final pgdog.toml and users.toml inside the container at boot from your prerequisite secrets, then execs PgDog. That is what keeps the credentials out of the Helm release.
  • Identity & policy — Grants the workload reveal on exactly the secrets it needs: the two chart-created ones, the admin password secret, and each pooled user’s credentials secret.

What Gets Created

  • Standard PgDog WorkloadRELEASE_NAME-pgdog, a stateless proxy serving TCP on port 6432.
  • Config SecretRELEASE_NAME-pgdog-config, an opaque secret holding the credential-free base pgdog.toml.
  • Startup Script SecretRELEASE_NAME-pgdog-startup, an opaque secret holding the script that assembles the config at container start.
  • Identity & PolicyRELEASE_NAME-pgdog-identity and RELEASE_NAME-pgdog-policy, granting reveal on the two chart secrets plus every prerequisite secret you referenced, and nothing else.
This template creates no secret that contains a credential. Your pooled-user and admin secrets are yours, which means uninstalling the release never destroys them.
This template does not create a GVC or a PostgreSQL instance. Deploy it into an existing GVC and point it at an existing PostgreSQL backend.

Prerequisites

Every secret you reference must exist before you install. Secrets are org-level, so no GVC flag is involved.
1

Create one credentials secret per pooled user

A dictionary secret holding exactly two keys — username and password. PgDog authenticates incoming clients with this pair and opens backend connections with it, so it must be a real PostgreSQL role on the backend:
Set users[0].credentialsSecretName to the name you used. Because the username travels inside the secret, you can point this straight at the secret your backend already uses — see Pooled Users.
2

Create the admin password secret

An opaque secret with encoding plain, whose payload is the password for PgDog’s admin database:
Set admin.passwordSecretName to the name you used. Use printf, not echoecho appends a newline, which cannot be carried in a TOML value. The startup script detects a trailing newline and fails at boot with that message rather than starting a proxy that rejects every admin login.
3

Read a secret back later

-o yaml is required. Without it the command prints the secret’s metadata table rather than its contents:
A missing prerequisite secret wedges the install rather than failing it. cpln helm install still exits 0 and reports success, every resource is created, and the workload then never starts — it sits at zero replicas. Because the container never ran, cpln logs returns zero lines, which reads as a broken platform rather than a missing prerequisite.The only diagnostic is status.versions[].message, which names the missing secret:
It is get-deployments — plain cpln workload get has no versions key at all. Creating the missing secret clears the wedge on its own, but slowly: recovery was measured at 7 minutes 37 seconds here, inside the 5.5–8.5 minute range seen across the catalog. A forced redeployment shortcuts it to roughly 90 seconds.

Installation

To install, follow the instructions for your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs
The proxy reaches ready roughly 25 seconds after install, once the startup script has assembled the config from your secrets and PgDog is listening on port 6432.

Upgrading From 1.0.0

Version 1.1.0 moved every credential out of Helm values. 1.0.0 shipped working defaults — a pooled-user password and an admin password published in the public template repository — and PgDog is precisely the thing applications put in their connection strings, so those values were the product rather than internal plumbing.
A helm upgrade that still carries the old values is rejected before anything is applied. Seven guard conditions stop the render, each naming its replacement. All of them were exercised against a real cpln helm upgrade, which failed client-side and left the running release untouched and healthy — no failed revision was created.This is deliberate: a silently ignored admin.password would leave you believing you had set a password you had not.
To upgrade an existing install:
1

Create the secrets

Follow Prerequisites. Put the credentials your applications already use into the pooled-user secret so existing connection strings keep working, and pick a fresh admin password — the old one was a published default.
2

Drop the removed keys from your values

Remove users[].name, users[].password and admin.password. Set users[].credentialsSecretName and admin.passwordSecretName instead, and rename resources.cpu and resources.memory to resources.maxCpu and resources.maxMemory if you had overridden them.
3

Upgrade

The upgrade replaces the single stateless replica. Client connections are dropped and reconnect against the new one; there is no data to migrate.

Configuration

The default values.yaml for this template:

Backend Databases

The databases list defines the PostgreSQL backends PgDog proxies. Each entry maps to a [[databases]] block in pgdog.toml. Multiple entries sharing the same name form a cluster — PgDog routes writes to primary backends and distributes reads across replica backends.
Using with the PostgreSQL template — Set host to RELEASE_NAME-postgres. That workload is stateful, so its short name resolves inside the GVC; the fully qualified RELEASE_NAME-postgres.GVC_NAME.cpln.local works too. Using with the PostgreSQL Highly Available template — Point the primary entry at the HA proxy (RELEASE_NAME-postgres-ha-proxy) and add replica entries using the replicaDirect hostnames (replica-N.RELEASE_NAME-postgres-ha.LOCATION.GVC_NAME.cpln.local).

Pooled Users

Each entry in users maps to a [[users]] block in users.toml. Only the routing target is a value — the username and password both come from the dictionary secret named by credentialsSecretName:
Because the username lives inside the secret rather than beside it, a user entry can point at the secret your PostgreSQL install already uses. PgDog authenticates the client with that pair and then opens its backend connections with the same pair, so it has to be a real PostgreSQL role — which is exactly what the PostgreSQL template’s own credentialsSecretName secret holds. Pointing both templates at one secret was tested end to end: PgDog reads only the username and password keys and ignores the extra database key that PostgreSQL stores alongside them. To pool a second user, create a second dictionary secret and add a second entry — one secret per entry:
The chart adds a policy target for each secret automatically, so nothing else needs changing.
Two entries may name the same secret, which is how you route one PostgreSQL role to two backend databases. The duplicate is deduplicated in the policy, so the secret appears as a single reveal target. There is deliberately no way to put two users in one secret — a secret holds one username/password pair.

Connection Pooling

The chart’s validation also accepts a third value, statement, which upstream PgDog documents but which has not been verified end to end on this platform. Stay on transaction or session unless you are prepared to test it yourself.

Timeouts

All timeout values are in milliseconds.

Load Balancing

readWriteSplit: include_primary allows the primary to also serve reads.

Admin Database

PgDog exposes an internal admin database for stats and introspection. The names are ordinary values; the password is a prerequisite secret:
Keeping it in its own secret is the point of the split: an application granted reveal on a pooled user’s credentials cannot also reach the admin database. Both directions were verified — the pooled user’s password is refused on the admin database, and the admin password is refused on a pooled database. Connect from a workload inside the GVC with any PostgreSQL client:
SHOW CLIENTS, SHOW POOLS and the other PgDog admin commands are available on that session.

Access

  • internalAccess.type — Controls which workloads can reach PgDog internally: same-gvc (default), same-org, workload-list, or none. With workload-list, name each caller in internalAccess.workloads as //gvc/GVC_NAME/workload/WORKLOAD_NAME.
  • publicAccess.enabled — When true, Control Plane provisions a public TCP load balancer on port 6432 and assigns a canonical *.cpln.app hostname automatically. Read it from status.canonicalEndpoint in cpln workload get RELEASE_NAME-pgdog --gvc GVC_NAME -o yaml.
  • publicAccess.address — Optional custom domain to attach when public access is enabled. It names an existing Control Plane domain resource.
Give the public endpoint time to come up. Flipping publicAccess.enabled on took 255 seconds to accept its first external connection in testing — longer than the 30–150 seconds most templates take. Budget anywhere from 30 seconds to about 5 minutes, and do not read a refused connection in the first few minutes as a broken setting.

Scaling

PgDog is stateless and scales horizontally by raising replicas. Each replica keeps its own connection pool, so reduce pooling.defaultPoolSize proportionally when adding replicas — otherwise the backend sees replicas × defaultPoolSize connections. The template ships and was tested at a single replica.

Logging

Connecting

Applications connect to PgDog exactly as they would connect to PostgreSQL — PgDog implements the full PostgreSQL wire protocol.
The in-GVC host must be fully qualified. The bare short name RELEASE_NAME-pgdog does not resolve — that shortcut only applies to stateful workloads, and PgDog runs as a standard one. A client using the short name fails with could not translate host name "RELEASE_NAME-pgdog" to address: Name or service not known, from anywhere in the GVC. Always use RELEASE_NAME-pgdog.GVC_NAME.cpln.local.
Example connection string, with the username and password taken from your credentials secret:

Important Notes

  • Create the prerequisite secrets before installing. A missing one wedges the deployment with no log output at all — Prerequisites gives the one command that diagnoses it.
  • PgDog does not manage PostgreSQL — it is a proxy only. Deploy a backend before pointing PgDog at it.
  • Port 6432, not 5432 — update application connection strings accordingly.
  • The in-GVC hostname must be fully qualifiedRELEASE_NAME-pgdog.GVC_NAME.cpln.local. The short name does not resolve for this workload.
  • Transaction mode drops session state — if your application relies on SET variables, temporary tables, or advisory locks, use pooling.mode: session.
  • Each replica keeps its own pool — when raising replicas, lower pooling.defaultPoolSize proportionally, or the backend sees replicas × defaultPoolSize connections.
  • Rotating a credential needs a restart — the config files are assembled once at container start, so run cpln workload force-redeployment RELEASE_NAME-pgdog --gvc GVC_NAME after changing a secret’s contents.
  • Enabling public access can take several minutes to take effect — measured at 255 seconds. Re-test before concluding the knob is broken.
  • Uninstall leaves your secrets in place — they are yours, created outside the release. Only the two chart-created config secrets are removed.

External References

PgDog Documentation

Official PgDog configuration and architecture reference

users.toml Reference

How PgDog maps pooled users onto backend databases

PgDog GitHub

Source code and issue tracker

PgDog Template

View the source files, default values, and chart definition

PostgreSQL Template

Single-instance PostgreSQL template for use with PgDog

PostgreSQL Highly Available Template

HA PostgreSQL with Patroni — primary and replicas for PgDog read/write splitting