"Don't run databases on Kubernetes" — and why that changed
For a long time the conventional wisdom was: Kubernetes is for stateless workloads; keep your database on a dedicated VM or a cloud-managed service. The reasoning was sound. Databases need stable identity, persistent storage, careful failover, ordered scaling, and operational rituals (backups, point-in-time recovery, version upgrades) that a generic scheduler doesn't understand.
Two things changed. First, Kubernetes grew the primitives stateful workloads need: StatefulSet for stable network identity and ordered rollout, PersistentVolumeClaim for durable storage, and the operator pattern for encoding operational knowledge. Second, projects like KubeBlocks built mature, database-aware operators on top of those primitives.
The building blocks underneath
Before KubeBlocks, understand what it builds on:
- StatefulSet gives each replica a stable name (
db-0,db-1) and stable storage, and rolls updates in order. Critical for clustered databases where node identity matters. - PersistentVolume / PersistentVolumeClaim decouple storage lifecycle from pod lifecycle. A pod can die and reschedule while its data survives on the same volume.
- Operators / Custom Resources let you describe a database declaratively (
kind: Cluster) and have a controller reconcile reality toward it — provisioning, healing, scaling.
What KubeBlocks adds
KubeBlocks is an open-source framework for running many database engines on Kubernetes with a *consistent* control plane. Rather than learning a different bespoke operator for Postgres, MySQL, MongoDB, and Redis, KubeBlocks gives you one set of abstractions:
- ClusterDefinition — a reusable description of an engine's topology (roles, components, how they connect).
- Cluster — your actual instance: "a 3-node PostgreSQL 16 with 20Gi storage."
- Backup/Restore — declarative scheduled and on-demand backups, with restore as a first-class operation.
- OpsRequest — declarative day-2 operations: vertical scale (more CPU/RAM), horizontal scale (more replicas), version upgrade, restart, failover.
The payoff is operational consistency. Scaling a MongoDB cluster and scaling a MySQL cluster look almost identical from the outside, even though the internals differ wildly.
A declarative cluster
Provisioning a PostgreSQL cluster becomes a YAML document instead of a runbook:
apiVersion: apps.kubeblocks.io/v1alpha1
kind: Cluster
metadata:
name: pg-prod
spec:
clusterDefinitionRef: postgresql
terminationPolicy: Delete
componentSpecs:
- name: postgresql
replicas: 3 # primary + 2 replicas
resources:
requests:
cpu: "2"
memory: 4Gi
volumeClaimTemplates:
- name: data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 20GiDay-2 operations are also declarative. Scaling up vertically:
apiVersion: apps.kubeblocks.io/v1alpha1
kind: OpsRequest
metadata:
name: pg-scale-up
spec:
clusterRef: pg-prod
type: VerticalScaling
verticalScaling:
- componentName: postgresql
requests:
cpu: "4"
memory: 8GiHow failover and HA work
For a replicated engine, KubeBlocks tracks roles (primary/secondary). When the primary's pod fails:
- 1The operator detects the unhealthy primary via probes.
- 2It promotes a healthy replica to primary (engine-specific promotion logic lives in the ClusterDefinition).
- 3It updates the service/endpoint so clients reconnect to the new primary.
- 4The failed node, when it returns, rejoins as a replica.
Because identity and storage are stable (StatefulSet + PVC), the recovered node keeps its data and catches up via replication rather than a full rebuild.
Backups and recovery
A managed database without good backups isn't managed. KubeBlocks supports:
- Scheduled backups on a cron, to object storage (S3/GCS-compatible).
- Manual/on-demand backups before risky changes.
- Restore into a new cluster from a backup.
apiVersion: apps.kubeblocks.io/v1alpha1
kind: BackupPolicy
metadata:
name: pg-backup
spec:
schedule:
cronExpression: "0 2 * * *" # nightly at 02:00
enable: trueEngines and the multi-engine advantage
| Engine | Common use | Topology managed |
|---|---|---|
| PostgreSQL | Relational, OLTP | Primary + replicas |
| MySQL | Relational, web apps | Primary + replicas |
| MongoDB | Document store | Replica set |
| Redis | Cache / ephemeral | Primary/replica or sentinel |
This is the architecture behind PandaStack's managed databases. We run PostgreSQL (14.x, 16.x), MySQL (5.7, 8.x), MongoDB, and Redis through KubeBlocks on GKE. A user clicks "create database," KubeBlocks provisions a cluster on the multi-region GKE backend, scheduled and manual backups are wired in, and the connection string is injected into the app as DATABASE_URL — so a connected service is talking to its database with zero manual config.
Managed-on-K8s vs cloud-managed: the honest trade-off
| Cloud-managed (RDS-style) | KubeBlocks on K8s | |
|---|---|---|
| Operational burden | Lowest (vendor runs it) | Operator runs it, you run operator |
| Portability | Locked to one cloud | Runs on any K8s |
| Cost control | Vendor pricing | Your infra, your nodes |
| Engine variety | Per-vendor menu | One control plane, many engines |
| Tuning depth | Limited knobs | Full access if needed |
KubeBlocks shines when you want cloud-managed *ergonomics* without cloud lock-in, or when you're building a platform that offers databases as a product. The trade-off: someone has to operate the operator and the storage layer, and free/small tiers are best treated as dev/hobby-sized (limited storage) rather than heavy production.
Practical guidance
- Use fast block storage (SSD-class StorageClass) — database IOPS live and die by the volume.
- Always enable scheduled backups before you put anything real on it.
- Plan capacity for failover — a 3-node cluster needs headroom to survive losing one.
- Pin engine versions and test upgrades via
OpsRequestin staging first.
References
- [KubeBlocks official documentation](https://kubeblocks.io/docs/preview/user_docs/overview/introduction)
- [KubeBlocks GitHub repository](https://github.com/apecloud/kubeblocks)
- [Kubernetes StatefulSet docs](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
- [Kubernetes Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
- [Kubernetes Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)
---
Want a managed Postgres, MySQL, MongoDB, or Redis without touching any of this? PandaStack provisions managed databases on KubeBlocks and auto-wires DATABASE_URL into your app. Try it free at [dashboard.pandastack.io](https://dashboard.pandastack.io).