High Availability PostgreSQL on Kubernetes

Kubernetes Series (Article 7): High Availability PostgreSQL on Kubernetes with Helm & Longhorn

After deploying a few applications, I realised I need a robust database system that I can confidently rely on for critical workloads. After some initial research, I finally settled on a reliable DB like PostgreSQL. Running a production-grade PostgreSQL cluster on Kubernetes doesn’t have to be complicated. In this post, I walk you through setting up a high-availability PostgreSQL cluster with:

  • Primary/replica replication
  • Persistent storage via Longhorn
  • Secure credential management via Kubernetes Secrets
  • Easy deployment using the Bitnami Helm chart

This setup is perfect for self-hosters, homelabbers, and professionals deploying apps like Vaultwarden, Gitea, or Nextcloud on Kubernetes.

By the end of this, you will have:

  • 1 PostgreSQL primary (read/write)
  • 3 PostgreSQL replicas (read-only)
  • Passwords are securely stored via Kubernetes Secrets
  • Data persisted with Longhorn
  • Predefined app user (sanju) and database (sanju_db)
  • Fully configurable with Helm

You should have:

Before you start:

  • A Kubernetes cluster (4+ nodes recommended)
  • Helm is installed and configured
  • Longhorn is installed with a default StorageClass
  • At least 32Gi of disk space available (8Gi x 4 pods)

Project Structure

  • values.yaml: Configuration for the PostgreSQL Helm chart
  • postgresql-admin-secret.yaml: Secret manifest for passwords (GitOps-friendly)

Step 1: Set Up PostgreSQL Admin Secret

Option A: Quick Setup with kubectl

kubectl create namespace postgresql

kubectl create secret generic postgresql-admin-secret \
  -n postgresql \
  --from-literal=postgres-password=SuperSecureAdminPass \
  --from-literal=password=SanjuAppUserPassword \
  --from-literal=replication-password=SanjuReplicationPassword
Bash

Option B: GitOps-Friendly YAML Manifest (My favourite)

Create a file postgresql-admin-secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: postgresql-admin-secret
  namespace: postgresql
type: Opaque
data:
  postgres-password: <base64-of-postgres-password>
  password: <base64-of-app-user-password>
  replication-password: <base64-of-replication-password>
YAML

Encode secrets using:

echo -n 'your-password' | base64
Bash

Apply it:

kubectl apply -f postgresql-admin-secret.yaml
Bash

Step 2: Deploy the PostgreSQL Cluster

Add the Bitnami Helm repo and install the chart:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

kubectl create namespace postgresql

helm install postgres-cluster bitnami/postgresql \
  -n postgresql \
  -f values.yaml
Bash

Service Discovery

ComponentDNS Name
Primary (RW)postgres-cluster-postgresql.postgresql.svc.cluster.local
Replicas (RO)postgres-cluster-postgresql-read.postgresql.svc.cluster.local

Step 3: Test Connectivity Inside the Cluster

Launch a client pod:

kubectl run pg-client -n postgresql --rm -it --image=bitnami/postgresql --restart=Never \
  --env="PGPASSWORD=SanjuAppUserPassword" -- bash
Bash

Then connect via psql:

psql -h postgres-cluster-postgresql.postgresql.svc.cluster.local -U sanju -d sanju_db
Bash

values.yaml Breakdown

Here’s a simplified view of the Helm values file:

architecture: replication

auth:
  enablePostgresUser: true
  username: sanju              # placeholder app-level user
  database: sanju_db                # placeholder app-level database
  replicationUsername: sanju_rep

  existingSecret: postgresql-admin-secret
  secretKeys:
    adminPasswordKey: postgres-password
    userPasswordKey: password
    replicationPasswordKey: replication-password

  usePasswordFiles: true

primary:
  persistence:
    enabled: true
    storageClass: longhorn
    accessModes:
      - ReadWriteOnce
    size: 5Gi

readReplicas:
  replicaCount: 3
  persistence:
    enabled: true
    storageClass: longhorn
    accessModes:
      - ReadWriteOnce
    size: 4Gi

replication:
  synchronousCommit: "off"
  numSynchronousReplicas: 0
  applicationName: postgres-cluster

containerPorts:
  postgresql: 5432
YAML

After the deployment, if you want to change or upgrade the deployment, you can use the command below:

helm upgrade --install postgres-cluster bitnami/postgresql \
  -n postgresql \
  -f values.yaml
Bash

By the end of this, you will have a working PostgreSQL DB running on your own Kubernetes cluster, and you will be able to use this DB to deploy other apps in the future.

I will soon write a post about deploying Vaultwarden, a self-hosted password manager, using this database.

Back to top arrow