Skip to content

NitroLine/pg-mini-operator

Repository files navigation

pg-mini-operator

A minimal Kubernetes operator that manages PostgreSQL declaratively through three custom resources:

  • PostgresInstance — provisions a running server: a Secret with generated credentials, a StatefulSet backed by a PersistentVolumeClaim, the Services needed to reach it, and an optional scheduled backup CronJob.
  • PostgresUser — creates a login role inside an instance and publishes a ready-to-mount credentials Secret.
  • PostgresDatabase — creates a database owned by a role, with a deletionPolicy that decides whether the real database is dropped on deletion.

Built with kubebuilder and controller-runtime. This is a learning project, so the code favours being readable over being feature-complete — see Roadmap for what is intentionally left out.

How it works

Applying a single PostgresInstance makes the operator reconcile five child objects, each owned by the instance (so they are garbage-collected when it is deleted):

PostgresInstance "demo"
├── Secret       demo-superuser        generated postgres password (created once)
├── Service      demo                  ClusterIP — stable address for clients
├── Service      demo-hl               headless — stable DNS identity for the pod
├── StatefulSet  demo                  postgres:<version>, 1 replica
│   └── PVC      data-demo-0           survives pod restarts
└── status       phase / endpoint / Ready condition

The reconcile loop is idempotent and level-based: it looks at the desired state (spec) and the real cluster state, makes them match, and records what it observed back into status.

PostgresUser and PostgresDatabase connect to the instance over the PostgreSQL protocol (via pgx) to run CREATE ROLE / CREATE DATABASE. Because a role or database lives inside Postgres — where the Kubernetes garbage collector cannot see it — each uses a finalizer to clean up on deletion. They also watch their referenced PostgresInstance, so a dependent that was waiting reconciles the moment the instance becomes Ready instead of polling.

Backups

When spec.backup is set, the operator maintains a CronJob that runs a small image (backup/, built from postgres:<version> plus the MinIO client). On each run it streams pg_dumpall | gzip straight into an S3-compatible bucket:

pg_dumpall | gzip | mc pipe s3/<bucket>/<instance>/<timestamp>.sql.gz

pg_dumpall captures every database and the global roles in one file, so a dump is a complete, restorable snapshot of the server. The Postgres password comes from the instance's superuser Secret; the S3 credentials come from the user-supplied Secret named by spec.backup.s3.credentialsSecretRef — the operator references both by name and never reads them itself. Clearing spec.backup deletes the CronJob.

Example

apiVersion: dbaas.nitroline.dev/v1alpha1
kind: PostgresInstance
metadata:
  name: demo
spec:
  version: "17"
  storage:
    size: 1Gi
---
apiVersion: dbaas.nitroline.dev/v1alpha1
kind: PostgresUser
metadata:
  name: app-user
spec:
  instanceRef: demo
  name: app_user
---
apiVersion: dbaas.nitroline.dev/v1alpha1
kind: PostgresDatabase
metadata:
  name: app-db
spec:
  instanceRef: demo
  name: app
  owner: app_user
  deletionPolicy: Retain   # keep the data if this resource is deleted
$ kubectl get postgresinstances
NAME   VERSION   PHASE     ENDPOINT
demo   17        Running   demo.default.svc.cluster.local:5432

$ kubectl get secret app-user-credentials -o jsonpath='{.data.password}' | base64 -d

The instance reports readiness through a standard condition, so it composes with kubectl wait:

$ kubectl wait postgresinstance/demo --for=condition=Ready --timeout=120s

API

PostgresInstance (dbaas.nitroline.dev/v1alpha1):

Field Required Description
spec.version yes PostgreSQL major version, used as the image tag
spec.storage.size yes PVC size, e.g. 1Gi
spec.storage.storageClassName no Storage class; cluster default when omitted
spec.resources no Container resource requests/limits
spec.backup.schedule no Cron schedule for pg_dumpall backups
spec.backup.s3 no S3 endpoint, bucket, credentialsSecretRef

Status exposes phase (Pending / Running / Failed), endpoint, and a Ready condition.

PostgresUser (dbaas.nitroline.dev/v1alpha1):

Field Required Description
spec.instanceRef yes Name of a PostgresInstance in the same namespace
spec.name yes Role name in PostgreSQL

Publishes a <name>-credentials Secret (username, password, endpoint, port) owned by the resource.

PostgresDatabase (dbaas.nitroline.dev/v1alpha1):

Field Required Description
spec.instanceRef yes Name of a PostgresInstance in the same namespace
spec.name yes Database name
spec.owner no Owning role; defaults to postgres
spec.deletionPolicy no Retain (default) keeps the data; Drop drops the DB

Getting started

Requires Go 1.24+, Docker, and a running cluster (minikube is fine).

# install the CRD
make install

# build the operator image and run it in the cluster
make docker-build deploy IMG=pg-mini-operator:dev

# create an instance, a user and a database
kubectl apply -f config/samples/
kubectl wait postgresinstance/postgresinstance-sample --for=condition=Ready --timeout=120s

Deleting an instance cascades to its owned objects (Secret, Services, StatefulSet, PVC, backup CronJob) via owner references. Deleting a PostgresUser drops its role; deleting a PostgresDatabase drops the database only when deletionPolicy: Drop — both via finalizers.

make undeploy
make uninstall

Backup demo

config/samples/demo/minio.yaml deploys an in-cluster MinIO (S3-compatible storage) plus a credentials Secret, so the whole backup path runs with no external dependencies:

kubectl apply -f config/samples/demo/minio.yaml
docker build -t pg-mini-operator-backup:dev ./backup   # build the backup image
# apply an instance whose spec.backup points at http://minio.default.svc:9000
kubectl create job manual-backup --from=cronjob/<instance>-backup   # trigger now

Roadmap

  • Streaming replication and failover.

Run make help for all available targets.

License

Apache 2.0.

About

Kubernetes operator for simple manage PostgreSQL

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors