PgDog Helm Chart
August 12, 2026 · View on GitHub
Production-ready Helm chart for PgDog with high availability, security, and resource management features.
Features
✅ Resource limits with guaranteed QoS (1GB:1CPU ratio)
✅ PodDisruptionBudget for high availability
✅ Pod anti-affinity for spreading across nodes
✅ ExternalSecrets integration for secure credential management
✅ ServiceAccount and RBAC with minimal permissions
✅ Pinned image versions for production deployments
Quick Start
- Install Helm
- Configure
kubectlto point to your K8s cluster - Add our Helm repository:
helm repo add pgdogdev https://helm.pgdog.dev - Configure databases and users in
values.yaml - Install:
helm install <name> pgdogdev/pgdog -f values.yaml
All resources will be created in <name> namespace.
Configuration
Configuration is done via values.yaml. All PgDog settings from
pgdog.toml and users.toml are supported. General settings
([general] section) are top level. Use camelCase format instead
of snake_case, for example: checkout_timeout becomes
checkoutTimeout.
Basic Example
workers: 2
defaultPoolSize: 15
openMetricsPort: 9090
logFormat: json
logLevel: info
Docker Image
By default, the chart uses the appVersion from Chart.yaml as the image
tag. This ensures the chart deploys a known-compatible version of PgDog
without requiring explicit configuration.
To override with a specific version:
image:
repository: ghcr.io/pgdogdev/pgdog
tag: "v1.2.3" # Pin to specific version
pullPolicy: IfNotPresent
To pin to an exact build, use a digest instead of a tag:
image:
repository: ghcr.io/pgdogdev/pgdog
digest: "sha256:abc123def456..." # Immutable reference
pullPolicy: IfNotPresent
When digest is specified, it takes precedence over tag.
Legacy format (still supported for backward compatibility):
image:
name: ghcr.io/pgdogdev/pgdog:main
pullPolicy: Always
Databases & Users
Add databases to databases list:
databases:
- name: "prod"
host: "10.0.0.1"
Add users to users list:
users:
- name: "alice"
database: "prod"
password: "hunter2" # See ExternalSecrets for secure storage
⚠️ For production: Use ExternalSecrets instead of plain text passwords (see ExternalSecrets section below).
Mirroring
Add mirrors to mirrors list. For example:
mirrors:
- sourceDb: "prod"
destinationDb: "staging"
High Availability
StatefulSet WAL storage
PgDog can persist its two-phase commit WAL when running as a StatefulSet.
Enable walPvc and configure the requested capacity:
statefulSet:
enabled: true
walPvc:
enabled: true
size: 5Gi
The StatefulSet creates one ReadWriteOnce persistent volume claim per replica
and mounts it at /var/lib/pgdog. Before PgDog starts, an init container creates
/var/lib/pgdog/wal on the volume. The generated PgDog configuration sets
two_phase_commit_wal_dir to that directory automatically. An explicit
twoPhaseCommitWalDir value still takes precedence.
The claims use the cluster's default StorageClass. Ensure one is configured
for dynamic provisioning before enabling walPvc. This option has no effect
when PgDog runs as a Deployment.
PodDisruptionBudget
Ensures minimum pod availability during voluntary disruptions (enabled by default):
podDisruptionBudget:
enabled: true
minAvailable: 1 # At least 1 pod always available
Pod Anti-Affinity
Spreads pods across nodes for better reliability (enabled by default):
podAntiAffinity:
enabled: true
type: soft # "soft" (preferred) or "hard" (required)
Config Change Restarts
By default, ConfigMap changes are not automatically picked up by
running pods. Enable restartOnConfigChange to trigger a rolling
restart whenever the rendered config changes:
restartOnConfigChange: true
This injects a checksum/config pod annotation that changes when the
config template output changes, causing Kubernetes to roll the pods.
ExternalSecrets Integration
Securely manage credentials using ExternalSecrets operator:
Option 1: Create ExternalSecret with chart
externalSecrets:
enabled: true
create: true
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
remoteRefs:
- secretKey: users.toml
remoteRef:
key: pgdog/users
Option 2: Use existing ExternalSecret
externalSecrets:
enabled: true
create: false
secretName: "my-secret" # Name of Secret you created
Referencing Existing Secrets
If you manage Kubernetes Secrets yourself (via kubectl, sealed-secrets,
SOPS, etc.), point the chart at them directly instead of putting secret
values in values.yaml. This works without the ExternalSecrets operator.
users.toml from an existing Secret
Set usersSecret.name to reference a Secret you created that holds the
users.toml file. The chart then skips rendering its own users Secret and
mounts yours instead:
usersSecret:
name: my-pgdog-users # existing Secret in the same namespace
key: users.toml # key holding the users.toml content (default: users.toml)
Create the Secret, for example:
kubectl create secret generic my-pgdog-users \
--from-file=users.toml=./users.toml
The value is mounted at /etc/secrets/pgdog/users.toml regardless of the
key name. A custom key is remapped automatically.
pgdog.toml from an existing Secret
Set configSecret.name to reference a Secret you created that holds the
pgdog.toml file. The chart then skips rendering its own pgdog.toml
ConfigMap and mounts your Secret as the config volume instead. Use this when
your pgdog.toml contains values that must not live in a ConfigMap, such as
database hosts or the admin password sourced from a secrets manager:
configSecret:
name: my-pgdog-config # existing Secret in the same namespace
key: pgdog.toml # key holding the pgdog.toml content (default: pgdog.toml)
Create the Secret, for example:
kubectl create secret generic my-pgdog-config \
--from-file=pgdog.toml=./pgdog.toml
The value is mounted at /etc/pgdog/pgdog.toml regardless of the key name.
A custom key is remapped automatically. All pgdog.toml-related chart values
(databases, defaultPoolSize, sharding settings, etc.) are ignored, since
your Secret provides the whole file.
Note: swapping the volume source is required — overlaying a Secret-provided
pgdog.toml on top of the chart's ConfigMap with a subPath volume mount
fails at container start (the mount target is a symlink inside the ConfigMap
volume; see
kubernetes/kubernetes#61545).
Note: plugins[].config entries render into the chart's ConfigMap and are
not mounted when configSecret.name is set. A Secret-provided pgdog.toml
controls its own plugin config paths, so mount plugin files elsewhere via
extraVolumes/extraVolumeMounts.
Datadog API key from an existing Secret
PgDog reads the Datadog API key from the DD_API_KEY environment variable.
Reference an existing Secret and the chart injects it as DD_API_KEY, so the
key is never written into pgdog.toml (or the ConfigMap):
otel:
endpoint: https://otlp.example.com/v1/metrics # your OTLP endpoint
datadogApiKeySecret:
name: my-datadog # existing Secret in the same namespace
key: dd-api-key # key holding the API key (default: dd-api-key)
Create the Secret, for example:
kubectl create secret generic my-datadog \
--from-literal=dd-api-key=<your-datadog-api-key>
This is mutually exclusive with the inline otel.datadogApiKey, which writes
the key into the ConfigMap as plaintext and should be avoided.
If both are set, the inline value takes precedence.
ServiceAccount & RBAC
RBAC with minimal permissions is enabled by default:
serviceAccount:
create: true
annotations: {}
rbac:
create: true
Resource Management
Default resources use Guaranteed QoS with 1GB:1CPU ratio:
resources:
requests:
cpu: 1000m # 1 CPU
memory: 1Gi # 1GB
limits:
cpu: 1000m
memory: 1Gi
You can set noCpuLimits: true to omit CPU limits. This allows containers to use idle CPU on the host.
noCpuLimits: true
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
memory: 512Mi
Prometheus (optional)
Prometheus metrics can be collected with a sidecar. Enable by
configuring prometheusPort:
prometheusPort: 9091
# Resources for Prometheus sidecar
prometheusResources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 100m
memory: 100Mi
Make sure it's different from openMetricsPort. You can configure
Prometheus in templates/prom/config.yaml.
Grafana Remote Write
To send metrics to Grafana Cloud or a Grafana instance, configure the remote write settings:
grafanaRemoteWrite:
url: "https://prometheus-prod-XX-XXX.grafana.net/api/prom/push"
basicAuth:
username: "123456" # Grafana Cloud user ID
password: "your-api-key" # Grafana Cloud API key
queueConfig:
capacity: 10000
maxShards: 50
minShards: 1
maxSamplesPerSend: 5000
batchSendDeadline: 5s
minBackoff: 30ms
maxBackoff: 5s
The queueConfig settings use Prometheus defaults and can be tuned
for performance. Remote write is automatically enabled when url is
set.
Plugins
Add plugins to the plugins list. Each plugin requires a name; config is
optional and accepts inline TOML content. When config is provided, a
<name>.toml file is added to the ConfigMap and mounted at
/etc/pgdog/<name>.toml.
plugins:
- name: pgdog_routing
config: |
[routing]
key = "value"
- name: pgdog_auth
TCP Keep-Alive Configuration
Configure socket-level TCP keep-alive behavior (optional):
tcpKeepalive: true
tcpTime: 7200000 # 2 hours (ms) before first keepalive probe
tcpInterval: 75000 # 75 seconds (ms) between keepalive probes
tcpRetries: 9 # Number of keepalive probes before connection is dropped
These settings control the TCP keep-alive behavior for database connections. All time values are in milliseconds. If not specified, system defaults are used.
Contributions
Contributions are welcome. Please open a pull request / issue with requested changes.
License
MIT