> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PgDog

> Deploy PgDog on Control Plane — a high-performance PostgreSQL connection pooler, load balancer, and sharding proxy written in Rust. Sits transparently in front of any PostgreSQL deployment and provides transaction or session pooling, automatic read/write splitting, and multi-replica load balancing.

## Overview

PgDog is a high-performance PostgreSQL connection pooler, load balancer, and sharding proxy 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. Works with any PostgreSQL-compatible backend including the Control Plane [PostgreSQL](/template-catalog/templates/postgres) and [PostgreSQL Highly Available](/template-catalog/templates/postgres-highly-available) templates, or any external PostgreSQL endpoint.

### Architecture

* **PgDog workload** — Stateless proxy that multiplexes client connections into a smaller pool of real backend connections. Scales horizontally; each replica maintains its own connection pool.
* **pgdog.toml** — Main configuration rendered as a secret and mounted at startup. Defines backend databases, pool settings, timeouts, and load balancing strategy.
* **users.toml** — Credentials configuration rendered as a separate secret. Defines which users can connect to PgDog and which backend databases they map to.

### What Gets Created

* **Standard PgDog Workload** — Stateless proxy workload listening on port 6432.
* **Identity & Policy** — Identity for the workload with access to credential secrets.
* **Secrets** — Two opaque secrets: one for `pgdog.toml` (database and pooling config) and one for `users.toml` (user credentials).

<Note>
  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.
</Note>

## Prerequisites

This template has no external prerequisites. To install, follow the instructions for your preferred method:

<CardGroup cols={2}>
  <Card title="UI" href="/template-catalog/install-manage/ui" icon="laptop">
    Browse, install, and manage templates visually
  </Card>

  <Card title="CLI" href="/template-catalog/install-manage/cli" icon="terminal">
    Manage templates from your terminal
  </Card>

  <Card title="Terraform" href="/template-catalog/install-manage/terraform" icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M77.941 44.5v36.836L46.324 62.918V26.082zm0 0" fill="#5c4ee5"/><path d="M81.41 81.336l31.633-18.418V26.082L81.41 44.5zm0 0" fill="#4040b2"/><path d="M11.242 42.36L42.86 60.776V23.941L11.242 5.523zm0 0M77.941 85.375L46.324 66.957v36.82l31.617 18.418zm0 0" fill="#5c4ee5"/></g></svg>}>
    Declare templates in your Terraform configurations
  </Card>

  <Card
    title="Pulumi"
    href="/template-catalog/install-manage/pulumi"
    icon={<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" id="Pulumi-Icon--Streamline-Svg-Logos" height="24" width="24">
    <desc>
        Pulumi Icon Streamline Icon: https://streamlinehq.com
    </desc>
    <path fill="#f26e7e" d="M4.683025 13.3318c0.869125 -0.5018 0.870575 -2.1264 0.003225 -3.62865s-2.27504 -2.313275 -3.1441725 -1.811475C0.672945 8.3935 0.6715 10.0181 1.53885 11.52035c0.86735 1.502275 2.27505 2.313275 3.144175 1.81145Zm0.0052 3.2167c0.86735 1.502275 0.865925 3.126875 -0.003225 3.628675 -0.86915 0.5018 -2.2768275 -0.309225 -3.144175 -1.81145 -0.8673525 -1.50225 -0.8659075 -3.126875 0.003225 -3.628675 0.8691325 -0.5018 2.276825 0.309225 3.144175 1.81145Zm5.922875 3.4243c0.86735 1.50225 0.8659 3.126775 -0.003225 3.62875 -0.869125 0.501775 -2.27685 -0.309325 -3.1442 -1.81155 -0.867325 -1.50225 -0.865875 -3.12685 0.00325 -3.628675 0.869125 -0.5018 2.276825 0.309225 3.144175 1.811475Zm-0.001925 -6.845275c0.86735 1.50225 0.8659 3.12685 -0.003225 3.628675 -0.869125 0.5018 -2.276825 -0.309225 -3.144175 -1.811475 -0.86735 -1.50225 -0.8659 -3.12685 0.003225 -3.62865 0.869125 -0.501825 2.276825 0.3092 3.144175 1.81145Z" stroke-width="0.25"></path>
    <path fill="#8a3391" d="M22.45775 11.524125c0.86725 -1.502225 0.865925 -3.12685 -0.003225 -3.62865 -0.869125 -0.501825 -2.276825 0.3092 -3.144175 1.811475 -0.86735 1.50225 -0.8659 3.126825 0.003225 3.62865 0.869125 0.501825 2.276825 -0.3092 3.144175 -1.811475Zm0.000175 3.2151c0.869075 0.5018 0.870625 2.1264 0.003225 3.62865 -0.86735 1.50225 -2.27505 2.313275 -3.144175 1.81145 -0.869125 -0.5018 -0.870575 -2.126425 -0.003225 -3.62865 0.86735 -1.50225 2.27505 -2.313275 3.144175 -1.81145ZM16.536225 18.157875c0.86915 0.501825 0.8706 2.126425 0.00325 3.628675 -0.86735 1.502125 -2.275075 2.313225 -3.1442 1.81145 -0.869125 -0.50175 -0.870575 -2.126425 -0.003225 -3.62865 0.867375 -1.502275 2.27505 -2.3133 3.144175 -1.811475Zm-0.003325 -6.843775c0.869125 0.5018 0.870575 2.126425 0.003225 3.628675s-2.27505 2.313275 -3.1442 1.811475c-0.869125 -0.501825 -0.870575 -2.126425 -0.003225 -3.628675 0.86735 -1.502275 2.27505 -2.313275 3.1442 -1.811475Z" stroke-width="0.25"></path>
    <path fill="#f7bf2a" d="M15.138225 2.06721c0 1.003615 -1.40625 1.817215 -3.14095 1.817215 -1.7347 0 -3.14095 -0.8136 -3.14095 -1.817215C8.856325 1.06359 10.262575 0.25 11.997275 0.25c1.7347 0 3.14095 0.81359 3.14095 1.81721ZM9.2166 5.482375c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172s1.40625 -1.817225 3.14095 -1.817225c1.7347 0 3.14095 0.8136 3.14095 1.817225Zm8.71005 1.8172c1.7347 0 3.14095 -0.813575 3.14095 -1.8172s-1.40625 -1.817225 -3.14095 -1.817225c-1.7347 0 -3.14095 0.8136 -3.14095 1.817225s1.40625 1.8172 3.14095 1.8172Zm-2.788425 1.605625c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172 0 -1.0036 1.40625 -1.8172 3.14095 -1.8172 1.7347 0 3.14095 0.8136 3.14095 1.8172Z" stroke-width="0.25"></path>
    </svg>}
  >
    Declare templates in your Pulumi programs
  </Card>
</CardGroup>

## Configuration

The default `values.yaml` for this template:

```yaml theme={null}
image: ghcr.io/pgdogdev/pgdog:v0.1.45

resources:
  cpu: 500m
  memory: 256Mi
  minCpu: 100m
  minMemory: 128Mi

replicas: 1

pooling:
  mode: transaction          # options: transaction, session
  defaultPoolSize: 10        # max server connections per pool
  minPoolSize: 1             # minimum idle connections kept open
  workers: 2                 # async threads; recommend 2× vCPU count

timeouts:
  connect: 5000              # time to establish a backend connection (ms)
  checkout: 5000             # max time a client waits for a free connection (ms)
  idle: 60000                # idle server connections closed after this (ms)
  query: 0                   # per-query timeout; 0 = disabled

loadBalancing:
  strategy: least_active_connections  # options: random, round_robin, least_active_connections
  readWriteSplit: include_primary

databases:
  - name: mydb
    host: postgres.example.com
    port: 5432
    role: primary            # options: primary, replica, auto

users:
  - name: myuser
    password: mypassword
    database: mydb

admin:
  database: admin
  user: admin
  password: changeme

auth:
  type: scram

logging:
  format: text               # options: text, json, json_flattened
  level: info

publicAccess:
  enabled: false
  # address: pgdog.example.com

internalAccess:
  type: same-gvc             # options: none, same-gvc, same-org, workload-list
  workloads: []
```

### 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.

```yaml theme={null}
databases:
  - name: mydb
    host: my-postgres.my-gvc.cpln.local
    port: 5432
    role: primary

  - name: mydb
    host: replica-1.my-patroni-postgres.aws-us-east-1.my-gvc.cpln.local
    port: 5432
    role: replica

  - name: mydb
    host: replica-2.my-patroni-postgres.aws-us-east-1.my-gvc.cpln.local
    port: 5432
    role: replica
```

| `role`    | Behavior                                                                    |
| --------- | --------------------------------------------------------------------------- |
| `primary` | Receives all write queries (`INSERT`, `UPDATE`, `DELETE`, DDL)              |
| `replica` | Receives read queries (`SELECT`) distributed by the load balancing strategy |
| `auto`    | PgDog detects the role via `pg_is_in_recovery()` at connection time         |

**Using with the PostgreSQL template** — Set `host` to `{release-name}-postgres.{gvc}.cpln.local`.

**Using with the PostgreSQL Highly Available template** — Point the `primary` entry at the HA proxy (`{release-name}-postgres-ha-proxy.{gvc}.cpln.local`) and add `replica` entries using the replicaDirect hostnames (`replica-{n}.{release-name}-postgres-ha.{location}.{gvc}.cpln.local`).

### Users

The `users` list defines which clients can connect to PgDog. Each entry maps to a `[[users]]` block in `users.toml`. The `database` field must match a `name` from the `databases` list.

```yaml theme={null}
users:
  - name: myuser
    password: mypassword
    database: mydb
```

PgDog uses the `name` and `password` to authenticate incoming clients, then uses the same credentials to connect to the backend PostgreSQL server.

### Connection Pooling

| Parameter                 | Default       | Description                                       |
| ------------------------- | ------------- | ------------------------------------------------- |
| `pooling.mode`            | `transaction` | Pool mode: `transaction` or `session`             |
| `pooling.defaultPoolSize` | `10`          | Maximum real Postgres connections per pool        |
| `pooling.minPoolSize`     | `1`           | Minimum idle connections kept open                |
| `pooling.workers`         | `2`           | Async threads; recommended value is 2× vCPU count |

| Pool Mode     | Description                                                                                                                                                                                               |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `transaction` | Backend connection held only for the duration of a transaction, then returned to the pool. Best for most web and API workloads. Not compatible with `SET` variables, temporary tables, or advisory locks. |
| `session`     | Backend connection held for the entire client session. Compatible with all Postgres features. Increase `defaultPoolSize` to match expected concurrent client count.                                       |

### Load Balancing

| Strategy                   | Description                                                                                     |
| -------------------------- | ----------------------------------------------------------------------------------------------- |
| `least_active_connections` | Routes reads to the replica with the fewest active connections. Recommended for most workloads. |
| `round_robin`              | Distributes reads evenly across all replicas in rotation.                                       |
| `random`                   | Selects a replica at random for each read query.                                                |

`readWriteSplit: include_primary` allows the primary to also serve reads when no replicas are available.

### Timeouts

All timeout values are in milliseconds.

| Parameter           | Default | Description                                         |
| ------------------- | ------- | --------------------------------------------------- |
| `timeouts.connect`  | `5000`  | Time to establish a backend connection              |
| `timeouts.checkout` | `5000`  | Max time a client waits for a free pool connection  |
| `timeouts.idle`     | `60000` | Idle backend connections closed after this duration |
| `timeouts.query`    | `0`     | Per-query timeout; `0` disables it                  |

### Admin Database

PgDog exposes an internal admin database for stats and introspection.

```yaml theme={null}
admin:
  database: admin
  user: admin
  password: changeme
```

<Warning>
  Always set `admin.password` explicitly. If omitted, PgDog generates a random password at each startup, making the admin database inaccessible across restarts.
</Warning>

Connect to the admin database with any PostgreSQL client:

```bash theme={null}
PGPASSWORD=<admin.password> psql \
  -h {release-name}-pgdog.{gvc}.cpln.local \
  -p 6432 \
  -U admin \
  -d admin
```

### Access

* `internalAccess.type` — Controls which workloads can reach PgDog internally: `same-gvc`, `same-org`, `workload-list`, or `none`.
* `publicAccess.enabled` — When `true`, Control Plane provisions a public TCP load balancer. The assigned hostname (e.g. `pgdog-name-hash.cpln.app:6432`) is visible in the Control Plane console or via `cpln workload get <name> -o yaml`.
* `publicAccess.address` — Optional custom domain to attach when public access is enabled.

### Scaling

PgDog is stateless and scales horizontally by increasing `replicas`. Each replica maintains its own connection pool — scale `pooling.defaultPoolSize` down proportionally when adding replicas to avoid overloading the backend with too many open connections.

### Logging

```yaml theme={null}
logging:
  format: text   # options: text, json, json_flattened
  level: info    # RUST_LOG syntax, e.g. info, debug, pgdog=debug
```

## Connecting

Applications connect to PgDog exactly as they would connect to PostgreSQL — PgDog implements the full PostgreSQL wire protocol.

| Setting  | Value                                          |
| -------- | ---------------------------------------------- |
| Host     | `{release-name}-pgdog.{gvc}.cpln.local`        |
| Port     | `6432`                                         |
| Database | A `name` from your `databases` list            |
| Username | A `name` from your `users` list                |
| Password | The matching `password` from your `users` list |

Example connection string:

```
postgresql://myuser:mypassword@{release-name}-pgdog.{gvc}.cpln.local:6432/mydb
```

## Important Notes

* **PgDog does not manage PostgreSQL** — it is a proxy only. Deploy a PostgreSQL backend separately before pointing PgDog at it.
* **Port 6432, not 5432** — PgDog listens on port 6432. Update application connection strings accordingly.
* **Transaction mode and session features** — If your application uses `SET` variables, prepared statements, temporary tables, or advisory locks, use `pooling.mode: session` instead of `transaction`.
* **Admin password** — Always set `admin.password` explicitly. Omitting it causes PgDog to generate a random password at each restart, making the admin database unreachable across restarts.
* **Scaling and pool sizing** — Each PgDog replica maintains its own pool. When scaling `replicas`, reduce `pooling.defaultPoolSize` proportionally to avoid opening too many total connections to the backend.

## External References

<CardGroup cols={2}>
  <Card title="PgDog Documentation" href="https://docs.pgdog.dev/" icon="dog">
    Official PgDog configuration and architecture reference
  </Card>

  <Card title="PgDog GitHub" href="https://github.com/pgdogdev/pgdog" icon="github">
    Source code and issue tracker
  </Card>

  <Card title="PostgreSQL Template" href="/template-catalog/templates/postgres" icon="database">
    Single-instance PostgreSQL template for use with PgDog
  </Card>

  <Card title="PostgreSQL Highly Available Template" href="/template-catalog/templates/postgres-highly-available" icon="database">
    HA PostgreSQL with Patroni — primary + replicas for PgDog read/write splitting
  </Card>
</CardGroup>
