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 chartpostgresql-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
BashOption 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>
YAMLEncode secrets using:
echo -n 'your-password' | base64
BashApply it:
kubectl apply -f postgresql-admin-secret.yaml
BashStep 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
BashService Discovery
Component | DNS 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
BashThen connect via psql
:
psql -h postgres-cluster-postgresql.postgresql.svc.cluster.local -U sanju -d sanju_db
Bashvalues.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
YAMLAfter 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
BashBy 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.