Running PostgreSQL on Minikube with CloudNativePG
Kubernetes has become the standard platform for running distributed, resilient workloads, and PostgreSQL is no exception. The Cloud Native PostgreSQL (CNPG) operator extends Kubernetes with custom resources that let you manage PostgreSQL clusters declaratively, handling everything from instance scaling to backups through ordinary manifests and kubectl commands.
Here is a practical walkthrough for getting the CNPG operator running on a local Minikube environment and deploying your first single-instance cluster.
Installing the Operator
The quickest way to install CNPG is to apply its core manifest directly from the project's release page:
kubectl apply -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.26/releases/cnpg-1.26.0.yaml
That single manifest provisions all of the operator's prerequisites:
- Dedicated namespace: The
cnpg-systemnamespace is created to host the operator components. - Custom Resource Definitions (CRDs): New resource types such as
Cluster,Backup,ScheduledBackup,Publication, andSubscriptionare registered, enabling you to manage PostgreSQL objects directly withkubectl. - RBAC policies: The necessary
ClusterRole,ClusterRoleBinding, andServiceAccountobjects are set up so the operator can interact with the cluster securely. - Admission webhooks: Mutating and validating configurations are deployed to check and, where needed, adjust custom resources as they are created or changed.
- Controller manager: The
cnpg-controller-managerdeployment is placed in thecnpg-systemnamespace. This component watches your CRDs and coordinates PostgreSQL operations.
Confirming the Operator Is Ready
After applying the manifest, check that the controller pod is running:
kubectl rollout status deployment -n cnpg-system cnpg-controller-manager
A successful rollout returns output similar to:
deployment "cnpg-controller-manager" successfully rolled out
Once the deployment reports ready, the operator can accept cluster definitions.
To keep subsequent commands concise and correctly scoped, it can help to set the default context to the cnpg-system namespace:
kubectl config set-context --current --namespace=cnpg-system
From then on, every kubectl command targets that namespace unless you override it with -n.
Defining a Simple PostgreSQL Cluster
The next step is to declare the database cluster itself. Save the following manifest as single.yaml:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 1
storage:
size: 1Gi
The manifest contains two core fields:
instances: 1sets the number of PostgreSQL pods to run.storage.size: 1Girequests a 1-gigabyte persistent volume for the database data.
Apply the manifest to create the cluster:
kubectl apply -f single.yaml
If accepted, you will see a confirmation similar to:
cluster.postgresql.cnpg.io/cluster-example created
The operator now handles provisioning the PostgreSQL instance. Verify the pod status with:
kubectl get pods
The output shows the cluster-example-1 pod running:
NAME READY STATUS RESTARTS AGE cluster-example-1 1/1 Running 0 2m
Connecting to the Database
With the pod up, open an interactive shell inside the container:
kubectl exec -it pod/cluster-example-1 -- /bin/bash
Once the shell prompt appears, launch the PostgreSQL client:
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
psql
psql (17.5 (Debian 17.5-1.pgdg110+1))
Type "help" for help.
postgres=# \du
List of roles
Role name | Attributes
-------------------+------------------------------------------------------------
app |
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
streaming_replica | Replication
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+---------+-------+--------+-----------+-----------------------
app | app | UTF8 | libc | C | C | | |
postgres | postgres | UTF8 | libc | C | C | | |
template0 | postgres | UTF8 | libc | C | C | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | C | C | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
(4 rows)
Because the cluster manifest did not specify any database options, CNPG automatically creates an app user and an app database, with the former as the owner of the latter. You can connect using that default user and database directly, or create additional roles and databases as needed.
What CNPG Brings to the Table
The process above shows how CNPG turns complex database administration into simple, repeatable operations. A production-grade PostgreSQL deployment, including scaling, backups, and failover in larger configurations, is expressed entirely in YAML and managed through the Kubernetes API. For a local environment such as Minikube, it provides a convenient way to experiment with cloud-native database workflows before moving to a full production cluster.



