Skip to content

Operators: the robot DBA/admin

An operator is a controller that knows how to run a specific piece of software. You don’t write a Deployment, a Service, a StatefulSet, and a backup cron by hand — you declare one high-level Custom Resource (CR) describing what you want, and the operator reconciles all the low-level objects to match. It’s a robot DBA/admin that watches its CR forever and keeps reality in sync.

flowchart LR
  CR[/Custom Resource<br/>e.g. Cluster, Keycloak/] -->|watches| Op[Operator]
  Op -->|creates + manages| Pods[Pods / StatefulSet]
  Op --> Svc[Services]
  Op --> Sec[(Secrets)]
  Op -.->|drift? recreate| Pods

You commit the CR to Git; Argo CD applies it (see GitOps); the operator does the rest. One operator, many instances — the same CNPG operator runs every Postgres on the cluster, no matter which app owns it.

OperatorCR you writeAPI group/versionNamespace
CloudNativePGClusterpostgresql.cnpg.io/v1cnpg-system
KeycloakKeycloakk8s.keycloak.org/v2beta1keycloak
Infisical Secrets OperatorInfisicalStaticSecret (+ InfisicalConnection, InfisicalAuth)secrets.infisical.com/v1beta1<app> (per app it serves)

A CNPG Cluster is tiny — instances, storage, and a backup target — yet it yields a running Postgres with WAL archiving:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: <app>-pg
namespace: <app>
spec:
instances: 1
bootstrap:
initdb:
database: <app>
owner: <app>

A Keycloak CR points the auth server at its database and hostname; the operator creates the StatefulSet and a keycloak-service for it (one instance per env namespace):

apiVersion: k8s.keycloak.org/v2beta1
kind: Keycloak
metadata:
name: keycloak
namespace: keycloak-<env>
spec:
instances: 1
db:
vendor: postgres
host: keycloak-pg-rw
database: keycloak

An InfisicalStaticSecret names a project/environment in Infisical and a target Secret to own; a sibling InfisicalAuth CR holds the Machine Identity credential it authenticates with. Unlike CNPG/Keycloak, this operator runs once but is configured per app — each app that wants GUI-managed secrets gets its own trio of CRs in each env namespace:

apiVersion: secrets.infisical.com/v1beta1
kind: InfisicalStaticSecret
metadata:
name: <app>-synced-secrets
namespace: <app>-<env>
spec:
infisicalAuthRef: { name: <app>-auth, namespace: <app>-<env> }
sources:
- projectId: "<project-id>"
environmentSlug: staging
secretPath: "/"
targets:
- kind: Secret
creationPolicy: Owner
name: <app>-api-kc
namespace: <app>-<env>

All three operators are installed declaratively via app-of-apps (apps/cnpg-operator, apps/keycloak-operator, apps/infisical-secrets-operator).

When CNPG creates a Cluster named <app>-pg, it also generates the ** <app>-pg-app** Secret automatically. That Secret holds the app user’s credentials and a ready-to-use uri key — which the API consumes as DB_URL:

env:
- name: DB_URL
valueFrom:
secretKeyRef:
name: <app>-pg-app # <cluster>-app, created by CNPG
key: uri

So the database password is never written anywhere by you — the operator mints it, and other workloads reference it by name. The Keycloak DB works the same way via keycloak-pg-app. See Secrets for the full picture.