finally fixed: run postgresql on kubernetes without pvb & keep your sanity
introduction: why everyone panics about postgresql on k8s
search for “postgresql on kubernetes” and you’ll see the same gloomy props: hundreds of yaml lines, persistentvolumeclaims (pvcs) piling up, and horror stories of data loss. as a devops full-stack coder, you want time for coding and seo, not to babysit volumes. good news: you can drop pvc/hostpath entirely for many workloads, keep your sanity, and still run a rock-solid postgresql cluster.
what to expect from this guide
- a brief locker-room tour of when state must live in k8s volumes
- the volume-less (pvc-less) pattern that relies on managed services and init containers
- copy-paste-ready yaml and commands for beginners
- tips to tune performance, monitor, and backup without losing your weekend to devops fire drills
stage 1: plan your data philosophy
rule of thumb
use pvc when:
- you can’t tolerate multi-minute latency (cold start of managed pg)
- you must stay self-hosted at the edge (air-gapped environments)
skip pvc when:
- you’re allowed to ride managed postgresql (cloud sql, rds, alloydb, aurora)
- your staging / development environments accept an occasional 3-5 min spin-up
never put production customer data on a container’s ephemeral storage—unless your managed host’s sla says it’s their data, not yours.
stage 2: pick your cloud sql or serverless shape
below table assumes google cloud sql, but the concept maps 1-to-1 in aws rds or azure db.
| workload | machine type | cloud sql tier | cost (usd / month) |
|---|---|---|---|
| dev / test | 1 vcpu, 1–2 gb ram | db-f1-micro | $7-$10 |
| small saas | 2 vcpu, 4 gb ram | db-g1-small | $25-$30 |
| medium prod | 4 vcpu, 16 gb ram | db-standard-4 | $100-$130 |
stage 3: let kubernetes find the database through secrets & config
apiversion: v1
kind: secret
metadata:
name: pg-credentials
type: opaque
stringdata:
postgres_user: "your_app"
postgres_password: "super-secure-pw"
postgres_db: "app_db"
postgres_host: "10.123.45.67" # cloud sql private ip
postgres_port: "5432"
if you prefer environment variables, mount the secret instead of hard-coding.
apiversion: apps/v1
kind: deployment
metadata:
name: web-api
spec:
template:
spec:
containers:
- name: app
image: mycompany/backend:latest
envfrom:
- secretref:
name: pg-credentials
ports:
- containerport: 8080
notice: zero pvc mounted—nothing to expand or back up on the cluster itself.
stage 4: tame migrations on first pod spin-up
skipping pvc means initcontainer runs migrations fast.
apiversion: batch/v1
kind: job
metadata:
name: db-migrate
spec:
template:
spec:
restartpolicy: onfailure
containers:
- name: migrate
image: mycompany/backend:latest
command: ["npx", "prisma", "migrate", "deploy"]
envfrom:
- secretref:
name: pg-credentials
hook the job inside your github actions or jenkins pipeline so your ci/cd flow triggers migrations automatically—classic devops.
stage 5: wire your application—minimal tcpping health check
readinessprobe:
exec:
command:
- /bin/sh
- -c
- "pg_isready -h ${postgres_host} -p ${postgres_port} -u ${postgres_user}"
initialdelayseconds: 5
periodseconds: 10
this lives in your deployment alongside the main container, no kyverno, istio, or pvc required!
stage 6: cost-optimized backups & monitoring without local disk
- auto-backup: flip the daily snapshot switch in the cloud sql console. export once a week with
pg_dump --no-ownerif you need open-source usda archives paranoid mode. - piggy-back observability: connect
pg_stat_statementsexporter to grafana cloud. done in 15 minutes.
stage 7: one-liners you’ll use every sprint
| scenario | command |
|---|---|
| connect from local | gcloud sql connect app-db --user=your_app --database=app_db |
| get credentials into shell | kubectl get secret/pg-credentials -o jsonpath='{.data.postgres_password}' | base64 -d |
| list running jobs | kubectl get jobs --field-selector=metadata.name=db-migrate |
common hurdles & quick fixes
- hurdle: “i thought once cloud name changed, all drivers times-out.”
fix: store hostname in k8s secret and reload pods viakubectl rollout restart deployment/web-api. - hurdle: slow cold boot scares qa during nightly integration tests.
fix: enable cloud sql serverless v2 (scale-to-zero kept off) or keep a small idle instance ($2/day guardrail). - hurdle: “we need read-replicas regionally.”
fix: one-click replica tier in cloud console; add reader endpoints to yourpostgres_host_readervariable—no pvc, no kubelets mutated.
tl;dr cheat sheet
- provision managed postgresql, **skip pvc**.
- store connection string in a k8s secret; mount via envfrom.
- run migrations inside **k8s job** so no local volume folders.
- backup and monitor outside the cluster—cheaper, cleaner, simpler docs for coding.
with this setup, postgresql becomes a world-class kitchen sink that doesn't leak into your cluster’s storage plumbing. happy coding, even happier seo headlines: “finally fixed: run postgresql on kubernetes without pvc & keep your sanity.”
Comments
Share your thoughts and join the conversation
Loading comments...
Please log in to share your thoughts and engage with the community.