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

推荐订阅源

F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
T
Tailwind CSS Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Secure Thoughts
博客园 - 【当耐特】
Know Your Adversary
Know Your Adversary
NISL@THU
NISL@THU
博客园 - 司徒正美
Last Week in AI
Last Week in AI
C
Cybersecurity and Infrastructure Security Agency CISA
P
Privacy & Cybersecurity Law Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
B
Blog
The GitHub Blog
The GitHub Blog
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Spread Privacy
Spread Privacy
Martin Fowler
Martin Fowler
博客园 - 叶小钗
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Tenable Blog
S
Securelist
博客园 - 三生石上(FineUI控件)
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Security Blog
Microsoft Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
罗磊的独立博客
T
Threat Research - Cisco Blogs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
F
Full Disclosure
Cloudbric
Cloudbric
The Cloudflare Blog
Y
Y Combinator Blog
Hugging Face - Blog
Hugging Face - Blog
Microsoft Azure Blog
Microsoft Azure Blog
H
Hacker News: Front Page
腾讯CDC
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
V2EX - 技术
V2EX - 技术
GbyAI
GbyAI
TaoSecurity Blog
TaoSecurity Blog
I
Intezer
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
Google Online Security Blog
Google Online Security Blog
T
The Blog of Author Tim Ferriss

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 Use Struct Tags in Go apt-key Deprecation: Add Repositories with GPG on Ubuntu Linux ps Command: 20 Real-World Examples Python struct.pack and struct.unpack for Binary Data Deadlock in Java: Examples, Detection, and Prevention How To Use Find and Locate to Search for Files on Linux Structured Resume Skill Extraction Using Mistral-7B Inference How to Use the Python Main Function How to Set Up NemoClaw on a DigitalOcean Droplet with 1-Click Build an End-to-End RAG Pipeline for LLM Applications From Single to Multi-Agent Systems: Key Infrastructure Needs Back Up Data to Object Storage Using Restic How to Generate Videos with LTX-2.3 on DigitalOcean GPU Droplets How To Install LAMP Stack (Apache, MySQL, PHP) on Ubuntu How to Download Files with cURL How To Use Variadic Functions in Go Generate UUIDs with uuidgen on Linux How To Use EJS to Template Your Node Application How to Install Node.js on Ubuntu (Step-by-Step Guide) MongoDB Indexes: Improve Query Performance with Node.js LLM Tool Calling with DigitalOcean AI Platform and Databases What are Text Diffusion Models? - An Overview Crafting a Game from Scratch with GPT-5.4 Building Long-Term Memory in AI Agents with LangGraph and Mem0 How To Install PHP 7.4 and Set Up a Local Development Environment on Ubuntu 20.04 Build a GraphQL API in Go to Upload Files to Spaces How To Lint and Format Code with ESLint in Visual Studio Code Train YOLO26 for Retail Object Detection on DigitalOcean GPUs How To Work with JSON in MySQL How to Use the JavaScript .map() Method Building a Scalable App with MongoDB Using DigitalOcean's MCP Server How to Create an SSH Key in Linux: Easy Step-by-Step Guide Measure MySQL Query Performance with mysqlslap How To Use *args and **kwargs in Python 3 Nemotron 3 helped me find the perfect dish rack? A2A vs MCP - How These AI Agent Protocols Actually Differ How To Install and Manage Supervisor Docker Container Images with Watchtower on Ubuntu Getting Started with Qwen3.5 Vision-Language Models How To Create a New Sudo-Enabled User on Ubuntu How to Use Ansible to Install and Set Up Docker on Ubuntu How To Enable Remote Desktop Protocol Using xrdp on Ubuntu 22.04 How To Convert a String to a List in Python How To Check If a String Contains Another String in Python How to Read a Properties File in Python Python Command Line Arguments: sys.argv, argparse, getopt Mastering Grep command in Linux/Unix: A Beginner's Tutorial Understanding Python Data Types How to Implement a Stack in C With Code Examples Python os.system() vs subprocess: Run System Commands How To Install and Use Docker Compose on Ubuntu How to Add and Delete Users on Ubuntu How To Order Query Results in Laravel Eloquent How To Define and Use Handlers in Ansible Playbooks How To Install and Use SQLite on Ubuntu How To Install and Use Homebrew on macOS How To Manage DateTime with Carbon in Laravel and PHP How To Install Git on Ubuntu How To Install and Secure Redis on Ubuntu How To Build and Install Go Programs on Linux Using ldflags to Set Version Information for Go Applications How To Build a Node.js Application with Docker How To Add JavaScript to HTML How To Reset Your MySQL or MariaDB Root Password How To Add Images in Markdown How To Set Up a Production Elasticsearch Cluster with Ansible How To Set Up a Firewall Using firewalld on CentOS Understanding Systemd Units and Unit Files How To Set Up Replication in MySQL How To Use the .htaccess File
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.