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 Reconciliation); 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.