DEV Community

iapilgrim
iapilgrim

Posted on

Running PostgreSQL on AKS with Premium SSD (StatefulSet + Azure Managed Disk)

In this tutorial, we’ll deploy PostgreSQL on AKS using:

  • StatefulSet
  • Azure Managed Disk (CSI)
  • ReadWriteOnce (RWO)
  • Premium SSD storage
  • Persistent volume validation

On Azure Kubernetes Service


🧠 Why Run PostgreSQL on AKS?

Common use cases:

  • SaaS apps with per-tenant DB
  • Internal microservice database
  • Dev/Test ephemeral environments
  • Platform engineering demos

Key benefits:

✅ Data survives pod restarts
✅ Azure-managed disk durability
✅ Zone-aware scheduling
✅ Snapshot + backup integration


🏗 Architecture Overview

PostgreSQL runs as:

  • StatefulSet
  • Backed by Azure Managed Disk
  • Using CSI driver
  • Access mode: ReadWriteOnce
  • Storage SKU: Premium_LRS

Kubernetes dynamically provisions the disk.


Step 1️⃣ — Verify StorageClass

AKS automatically creates a default CSI storage class.

Check it:

kubectl get storageclass
Enter fullscreen mode Exit fullscreen mode

Then inspect:

kubectl describe storageclass default
Enter fullscreen mode Exit fullscreen mode

Look for:

Provisioner: disk.csi.azure.com
skuName: Premium_LRS
Enter fullscreen mode Exit fullscreen mode

If you see Premium_LRS, you're using Premium SSD.


Step 2️⃣ — Create Namespace

kubectl create namespace postgres-demo
Enter fullscreen mode Exit fullscreen mode

Step 3️⃣ — Create Secret

apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
  namespace: postgres-demo
type: Opaque
stringData:
  POSTGRES_PASSWORD: supersecurepassword
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f secret.yaml
Enter fullscreen mode Exit fullscreen mode

Step 4️⃣ — Create Headless Service

StatefulSets require a headless service.

apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: postgres-demo
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
    - port: 5432
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f service.yaml
Enter fullscreen mode Exit fullscreen mode

Step 5️⃣ — Deploy PostgreSQL StatefulSet

⚠ Important: Azure disks contain a lost+found directory at root.

PostgreSQL refuses to initialize if the directory is not empty.

We fix this by using a subdirectory via PGDATA.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: postgres-demo
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:15
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: POSTGRES_PASSWORD
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        volumeMounts:
        - name: postgres-data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: postgres-data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: default
      resources:
        requests:
          storage: 10Gi
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f statefulset.yaml
Enter fullscreen mode Exit fullscreen mode

Step 6️⃣ — Verify Deployment

Wait for pod:

kubectl rollout status statefulset/postgres -n postgres-demo
Enter fullscreen mode Exit fullscreen mode

Check PVC:

kubectl get pvc -n postgres-demo
Enter fullscreen mode Exit fullscreen mode

Check pod:

kubectl get pods -n postgres-demo
Enter fullscreen mode Exit fullscreen mode

You should see:

postgres-0   Running
Enter fullscreen mode Exit fullscreen mode

Step 7️⃣ — Confirm Azure Disk Creation

In Azure Portal:

  1. Open resource group starting with:
   MC_<your-rg>_<cluster>_<region>
Enter fullscreen mode Exit fullscreen mode
  1. Find the Managed Disk
  2. Verify:
  • SKU = Premium SSD
  • Size = 10Gi
  • Attached to AKS node

AKS automatically:

  • Created disk
  • Attached it
  • Mounted it
  • Bound PVC to Pod


Step 8️⃣ — Test Persistence

Enter PostgreSQL:

kubectl exec -it postgres-0 -n postgres-demo -- psql -U postgres
Enter fullscreen mode Exit fullscreen mode

Create database:

CREATE DATABASE demo;
Enter fullscreen mode Exit fullscreen mode

Now delete pod:

kubectl delete pod postgres-0 -n postgres-demo
Enter fullscreen mode Exit fullscreen mode

Wait for restart:

kubectl rollout status statefulset/postgres -n postgres-demo
Enter fullscreen mode Exit fullscreen mode

Verify:

kubectl exec -it postgres-0 -n postgres-demo -- psql -U postgres -l
Enter fullscreen mode Exit fullscreen mode

You should still see:

demo
Enter fullscreen mode Exit fullscreen mode

✅ Data persisted
✅ Disk reattached automatically
✅ No data loss


🔍 What This Demonstrates

Feature Why It Matters
StatefulSet Stable volume binding
RWO Single-writer DB safety
Azure CSI Dynamic disk provisioning
Premium SSD Low latency production storage
PGDATA fix Proper Azure disk compatibility

🏆 Production Improvements

For real workloads, add:

  • Resource limits
  • Liveness/readiness probes
  • PodDisruptionBudget
  • Anti-affinity rules
  • Replica for HA
  • Backup strategy (Velero or Azure Backup)
  • Monitoring (Azure Monitor / Prometheus)

🎯 Final Architecture

PostgreSQL (StatefulSet)

PVC (RWO)

Azure Managed Disk (Premium SSD)

Zone-aware AKS node


🚀 When to Use This Pattern

Use PostgreSQL on AKS if:

  • You need full control
  • You want per-tenant DB isolation
  • You want environment parity (dev → prod)
  • You’re building platform-level tooling

Use managed DB service instead if:

  • You want automatic patching
  • You want built-in HA
  • You don’t want operational overhead

🔚 Conclusion

You’ve now:

✔ Deployed PostgreSQL on AKS
✔ Used Azure Premium SSD
✔ Configured StatefulSet properly
✔ Solved lost+found issue
✔ Verified data persistence

This is the foundation of running stateful workloads on AKS safely and correctly.

Top comments (0)