惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
博客园_首页
IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
大猫的无限游戏
大猫的无限游戏
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 司徒正美
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
Jina AI
Jina AI
月光博客
月光博客
小众软件
小众软件
S
SegmentFault 最新的问题
量子位
阮一峰的网络日志
阮一峰的网络日志
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

DigitalOcean Community Tutorials

Mastering grep with Regular Expressions for Efficient Text Search It's Time to Break Up with Your Cloud: Why AI Teams are Switching We Built a Private-Document AI App to Test Platform Security. Here Is What We Could Actually Verify. PostgreSQL Explained: A Complete Beginner-to-Advanced Guide How To Install and Configure Postfix on Ubuntu How To Build a Web Application Using Flask in Python 3 Build AI Reading List with DigitalOcean Functions and Mistral How To Concatenate Strings in Python How to Allow MySQL Remote Access Securely How To Install and Use Docker on Rocky Linux How To Build a Multi-Agent AI System with Docker Agent DSPy Use Cases: Build Optimized LLM Pipelines How To Submit AJAX Forms with jQuery Build an AI-Powered GPU Fleet Optimizer with the DigitalOcean AI Platform ADK Monitor GPU Utilization in Real Time: A Complete Guide Reduce File Size of Images in Linux - CLI and GUI methods Reduce PDF File Size in Linux: Tools and Methods How To Set Up a Private Docker Registry on Ubuntu How To Troubleshoot Terraform: Errors and Fixes How to Use Go Modules Python Multiprocessing Example: Process, Pool & Queue Convert Class Components to Functional Components with React Hooks How To Install and Configure Ansible on Ubuntu LLM Tokenizers Simplified: BPE, SentencePiece, and More How To Monitor System Authentication Logs on Ubuntu How to Use Traceroute and MTR to Diagnose Network Issues Importing Packages in Go: A Complete Guide Create RAID Arrays with mdadm on Ubuntu How To Make an HTTP Server in Go How To Set Up Time Synchronization on Ubuntu
How to Deploy Postgres to Kubernetes Cluster
Anish Singh Walia · 2026-03-26 · via DigitalOcean Community Tutorials

Introduction

How to Deploy Postgres to Kubernetes Cluster is a common question for teams that want database control while keeping app and infrastructure workflows in one platform. In this tutorial, you will deploy PostgreSQL on Kubernetes with a StatefulSet, persistent volumes, Secrets, and Services, then verify that data survives pod restarts.

You will also compare manual YAML, Helm, and operator-based approaches so you can choose the right path for development, staging, or production on DigitalOcean Kubernetes (DOKS).

Key takeaways

  • PostgreSQL should run in a StatefulSet on Kubernetes because it needs stable identity and durable storage.
  • On DOKS, use block storage classes for persistent database volumes and keep ReadWriteOnce access mode for primary instances.
  • Store passwords in Kubernetes Secret objects, not ConfigMaps.
  • Manual YAML gives maximum control, Helm gives faster setup, and operators are best when you need lifecycle automation at scale.
  • For production workloads, plan backups, failover, and recovery tests before go-live.

Prerequisites

Before you begin, make sure you have:

Understanding how Postgres runs on Kubernetes

PostgreSQL is stateful. That means storage persistence, startup ordering, and identity matter. Kubernetes supports this well when you use the right controllers and storage model.

Why StatefulSet is required for PostgreSQL

A StatefulSet gives each pod a stable name and stable storage attachment behavior, which fits a database workload. A standard Deployment treats pods as interchangeable units and is a better fit for stateless services.

How persistent volumes work for database storage

Kubernetes uses a PersistentVolumeClaim (PVC) to request durable storage from a StorageClass. On DOKS, this maps to DigitalOcean Block Storage. If the pod is rescheduled, Kubernetes reattaches the same volume so your data remains available.

Deployment method comparison

Method Complexity HA Production fit Best for
Manual YAML Medium Manual setup Good with strong ops Full control
Helm chart Low-mid Chart based Good for standard setups Fast rollout
Operator Mid-high Built in ops Best for long-term ops Many clusters

Step 1: Create a namespace

Create a dedicated namespace so database resources stay isolated from application resources.

apiVersion: v1
kind: Namespace
metadata:
  name: postgres-demo

Apply it:

kubectl apply -f namespace.yaml

Step 2: Create a Secret and ConfigMap

Use a Secret for passwords and a ConfigMap for non-sensitive settings.

Create the Secret:

kubectl create secret generic postgres-auth \
  --namespace postgres-demo \
  --from-literal=POSTGRES_PASSWORD='ReplaceWithStrongPassword'

Create postgres-configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-config
  namespace: postgres-demo
data:
  POSTGRES_DB: appdb
  POSTGRES_USER: appuser

Apply it:

kubectl apply -f postgres-configmap.yaml

Step 3: Create persistent storage

For DOKS, use do-block-storage for most cases. For production environments where you want to avoid accidental volume deletion, consider do-block-storage-retain.

Create postgres-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: postgres-demo
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: do-block-storage
  resources:
    requests:
      storage: 20Gi

Apply and verify:

kubectl apply -f postgres-pvc.yaml
kubectl get pvc -n postgres-demo

Step 4: Create Services for PostgreSQL

Use a headless service for stable network identity in the StatefulSet and a ClusterIP service for in-cluster client access.

Create postgres-services.yaml:

apiVersion: v1
kind: Service
metadata:
  name: postgres-headless
  namespace: postgres-demo
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
    - name: postgres
      port: 5432
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: postgres-demo
spec:
  type: ClusterIP
  selector:
    app: postgres
  ports:
    - name: postgres
      port: 5432
      targetPort: 5432

Apply:

kubectl apply -f postgres-services.yaml

Step 5: Deploy PostgreSQL with a StatefulSet

This StatefulSet deploys a single PostgreSQL primary with explicit CPU and memory requests and limits for predictable scheduling.

Create postgres-statefulset.yaml:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: postgres-demo
spec:
  serviceName: postgres-headless
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef:
                  name: postgres-config
                  key: POSTGRES_DB
            - name: POSTGRES_USER
              valueFrom:
                configMapKeyRef:
                  name: postgres-config
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-auth
                  key: POSTGRES_PASSWORD
          resources:
            requests:
              cpu: "250m"
              memory: "512Mi"
            limits:
              cpu: "1"
              memory: "1Gi"
          readinessProbe:
            exec:
              command:
                - /bin/sh
                - -c
                - pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            exec:
              command:
                - /bin/sh
                - -c
                - pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
            initialDelaySeconds: 30
            periodSeconds: 10
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-pvc

Apply and verify:

kubectl apply -f postgres-statefulset.yaml
kubectl get pods -n postgres-demo -l app=postgres
kubectl get statefulset -n postgres-demo

Step 6: Connect to PostgreSQL and verify persistence

Now test connectivity and verify that data survives pod recreation.

Get the pod name:

POD_NAME=$(kubectl get pods -n postgres-demo -l app=postgres \
  -o jsonpath='{.items[0].metadata.name}')
echo "$POD_NAME"

Connect to PostgreSQL:

kubectl exec -it -n postgres-demo "$POD_NAME" -- \
  psql -U appuser -d appdb

Run a quick test inside psql:

CREATE TABLE IF NOT EXISTS healthcheck (
  id serial PRIMARY KEY,
  status text NOT NULL
);
INSERT INTO healthcheck (status) VALUES ('ok');
SELECT count(*) FROM healthcheck;

Exit psql, then delete the pod and wait for recreation:

kubectl delete pod -n postgres-demo "$POD_NAME"
kubectl get pods -n postgres-demo -l app=postgres -w

Reconnect and verify the row is still there:

NEW_POD_NAME=$(kubectl get pods -n postgres-demo -l app=postgres \
  -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it -n postgres-demo "$NEW_POD_NAME" -- \
  psql -U appuser -d appdb -c "SELECT count(*) FROM healthcheck;"

If you still see the row count, your storage is persistent and correctly attached.

Step 7: Back up and restore a PostgreSQL database

You can perform a quick logical backup with pg_dump. For production strategy, also review How To Back Up and Restore a PostgreSQL Database.

Create a backup:

kubectl exec -n postgres-demo "$NEW_POD_NAME" -- \
  pg_dump -U appuser -d appdb > appdb_backup.sql

Restore from backup:

kubectl cp appdb_backup.sql postgres-demo/"$NEW_POD_NAME":/tmp/appdb_backup.sql
kubectl exec -n postgres-demo "$NEW_POD_NAME" -- \
  psql -U appuser -d appdb -f /tmp/appdb_backup.sql

Alternative method: Deploy PostgreSQL with Helm

If you want a faster install path, Helm is a good option:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install postgres bitnami/postgresql \
  --namespace postgres-helm \
  --create-namespace \
  --set auth.postgresPassword='ReplaceWithStrongPassword' \
  --set primary.persistence.storageClass=do-block-storage \
  --set primary.resources.requests.cpu=250m \
  --set primary.resources.requests.memory=512Mi

Verify Helm deployment:

helm list -n postgres-helm
kubectl get pods -n postgres-helm

Operator-based deployments overview

Kubernetes operators automate lifecycle tasks such as backup scheduling, failover, rolling updates, and major operational workflows that are hard to maintain in plain YAML.

  • CloudNativePG is a CNCF incubating PostgreSQL operator and a strong choice for production-ready automation.
  • Zalando Postgres Operator is widely used and supports HA topologies backed by Patroni.

Use an operator when you need repeatable cluster operations, built in failover workflows, and easier day-2 management.

High availability and production considerations

Running Postgres on Kubernetes in production is possible, but your architecture must include failure handling.

  • Use replication with at least one standby.
  • Decide between asynchronous and synchronous replication based on your write latency and data loss tolerance.
  • Define backup, restore, and disaster recovery runbooks.
  • Monitor storage IOPS, replication lag, and restart behavior.
  • Plan maintenance windows for major PostgreSQL upgrades.

If your team prefers less operational overhead, compare this self-managed setup with DigitalOcean Managed PostgreSQL, which provides managed backups, updates, and high availability controls.

FAQs

1. What is the purpose of a Kubernetes StatefulSet?

A StatefulSet is used for workloads that need stable pod identity and stable storage. PostgreSQL depends on both, so StatefulSet is the standard controller for Kubernetes database pods.

2. What is a StatefulSet vs Deployment for PostgreSQL?

A Deployment is designed for stateless replicas that can be replaced freely. A StatefulSet preserves pod identity and works with persistent volumes in a way that is safer for database workloads.

3. Is it safe to delete a StatefulSet?

You can delete a StatefulSet object, but do it carefully. Depending on your delete command and retention settings, pods and data volumes may be affected. In production, verify reclaim policy, backups, and restore steps before deletion.

4. Can I run Postgres on Kubernetes in production?

Yes. Many teams run production Postgres on Kubernetes, especially with operators. The key is disciplined storage, backup, failover, monitoring, and upgrade practices.

5. What storage class should I use for Postgres on DOKS?

Use do-block-storage for general workloads. For production data retention safety, evaluate do-block-storage-retain so volume deletion is less likely during PVC lifecycle changes.

Conclusion

In this tutorial, you deployed PostgreSQL on Kubernetes using a StatefulSet, durable storage, and secure configuration handling. You also validated connectivity and data persistence, compared deployment methods, and reviewed production planning requirements.

Next steps

To keep building from here, explore related DigitalOcean resources:

Still looking for an answer?

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.