Skip to content

pgdogdev/helm

Repository files navigation

PgDog Helm Chart

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

  1. Install Helm
  2. Configure kubectl to point to your K8s cluster
  3. Add our Helm repository: helm repo add pgdogdev https://helm.pgdog.dev
  4. Configure databases and users in values.yaml
  5. 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

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.

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