Skip to content

PostgreSQL on Docker and Kubernetes

★★★★★ Intermediate

Running PostgreSQL in containers requires careful handling of persistent storage, shared memory, and stateful workload patterns. Kubernetes adds orchestration complexity but enables automated HA.

Docker

# Run PostgreSQL in Docker
docker run -d --name pg \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  --shm-size=256m \
  postgres:15

# Connect
psql -h localhost -U postgres

Production considerations: - Always use named volumes (not bind mounts) for data directory - --shm-size=256m for larger shared memory (default 64MB too small) - Set shared_buffers inside container to match allocated memory - Health checks: pg_isready -U postgres

Kubernetes Core Resources

StatefulSet: For stateful applications like databases. Guarantees: stable network identifiers, stable persistent storage, ordered graceful deployment/scaling.

PersistentVolumeClaim: Request for storage.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    spec:
      containers:
      - name: postgres
        image: postgres:15
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: pg-secret
              key: password
        volumeMounts:
        - name: pgdata
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: pgdata
    spec:
      accessModes: [ReadWriteOnce]
      resources:
        requests:
          storage: 50Gi
      storageClassName: fast-ssd

Storage Class

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd
reclaimPolicy: Retain  # CRITICAL: don't delete data when PV released
volumeBindingMode: WaitForFirstConsumer

HA on Kubernetes

Bitnami Helm Chart

helm repo add bitnami https://charts.bitnami.com/bitnami
helm install pgsql-ha bitnami/postgresql-ha \
  --set service.type=LoadBalancer
# Architecture: Pgpool-II + postgresql-repmgr

Zalando PostgreSQL Operator

apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: acid-cluster
spec:
  teamId: "myteam"
  numberOfInstances: 3
  postgresql:
    version: "15"
  volume:
    size: 50Gi
  resources:
    requests:
      cpu: "1"
      memory: 2Gi

GKE Patroni Deployment

gcloud container clusters create pg-cluster --zone us-central1-c --machine-type e2-medium --num-nodes 3

helm install patroni ./patroni \
  --set credentials.superuser="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32)"

# Check roles
kubectl get pods -l spilo-role -L spilo-role

Monitoring Stack

  • Prometheus + pg_exporter: Connections, query latency, replication lag, table bloat
  • Grafana: PostgreSQL performance dashboards
  • pg_stat_statements: Track query performance
  • pgBadger: Log analyzer for slow query reports

Key Facts

  • PV alone is not backup - always backup to external storage (S3/GCS)
  • Use volumeBindingMode: WaitForFirstConsumer for regional clusters
  • Pod deletion triggers failover in HA setups
  • Crunchy PGO: production-grade operator with pgBackRest integration
  • ConfigMap for postgresql.conf, Secret for passwords

Gotchas

  • Default Docker --shm-size (64MB) is insufficient for shared_buffers > 128MB
  • reclaimPolicy: Delete (default) destroys data when PV released - use Retain for databases
  • StatefulSet scaling down does NOT delete PVCs - orphaned volumes accumulate
  • gcloud compute disks list after cluster deletion to check for leftover disks
  • Kubernetes DNS: service-name.namespace.svc.cluster.local:5432

See Also