CloudNativePG Operator Controls#
CloudNativePG exposes several annotation-based "escape hatches" that let operators pause or narrow the scope of automated reconciliation. These controls live in the cnpg.io/ annotation namespace and are applied directly to Cluster (or Pooler) resources via kubectl annotate. They are designed for emergency scenarios — debugging a malfunctioning instance, preventing restarts during a sensitive manual operation, or temporarily freezing a cluster.
All annotation constants are defined in pkg/utils/labels_annotations.go.
cnpg.io/reconciliationLoop#
Scope: Cluster
Value: disabled
Setting this annotation to disabled on a Cluster object causes the operator to skip the entire reconciliation loop for that cluster. The check is the very first thing done inside reconcile() — if the annotation is present, the reconciler logs a warning and returns immediately without touching any cluster resources .
The helper function IsReconciliationDisabled() performs this check against the ReconciliationLoopAnnotationName constant. Use this when you need to manually inspect or modify the cluster without the operator overwriting your changes.
# Disable
kubectl annotate cluster <name> cnpg.io/reconciliationLoop=disabled --overwrite
# Re-enable (remove annotation)
kubectl annotate cluster <name> cnpg.io/reconciliationLoop-
cnpg.io/fencedInstances#
Scope: Cluster
Value: JSON list of instance names, e.g. '["cluster-example-1"]'; use '["*"]' to fence the entire cluster
Fencing shuts down the PostgreSQL postmaster process on the targeted instance(s) via a fast→immediate shutdown sequence , while the pod stays running (but is removed from the Ready endpoints). This lets you exec into the pod and inspect or repair the data directory manually without the operator or PostgreSQL interfering. Metrics collection pauses except for cnpg_collector_fencing_on.
⚠️ Fencing the primary does not trigger failover. The operator intentionally skips switchover when the current primary is fenced — see handleSwitchover(). This is deliberate: the cluster is "frozen" until you explicitly unfence.
The annotation constant is FencedInstanceAnnotation. Core implementation:
GetFencedInstances()— parses the JSON annotation into a string setAddFencedInstance()— adds an instance name (or*) to the annotationFencingMetadataExecutor— fluent builder that patches the Cluster objectIsFenced()/SetFencing()— atomic instance-level stateRequestFencingOn()/RequestAndWaitFencingOff()— sendsfenceOn/fenceOffcommands to the lifecycle goroutineHandleInstanceCommandRequests()— processes those commands, callingTryShuttingDownFastImmediate()- The lifecycle run loop checks
IsFenced()before starting PostgreSQL and skips startup if true
Preferred CLI (via the kubectl cnpg plugin):
# Fence one instance
kubectl cnpg fencing on <cluster-name> --instance <instance-name>
# Fence the whole cluster
kubectl cnpg fencing on <cluster-name>
# Lift fencing
kubectl cnpg fencing off <cluster-name> --instance <instance-name>
Or manually:
kubectl annotate cluster <name> cnpg.io/fencedInstances='["cluster-example-1"]' --overwrite
cnpg.io/reconcilePodSpec#
Scope: Cluster, Pooler
Value: disabled
Prevents the operator from restarting instances when it detects a drift between the current PodSpec and what the cluster spec would generate . Useful when you need to make a temporary, operator-managed pod change without triggering rolling restarts.
- On
Cluster: operator skips pod restarts for any PodSpec-level changes (env vars, images, resource requests, etc.) - On
Pooler: operator restricts deployment spec modifications tospec.instanceschanges only
The check is IsPodSpecReconciliationDisabled() .
kubectl annotate cluster <name> cnpg.io/reconcilePodSpec=disabled --overwrite
cnpg.io/hibernation#
Scope: Cluster
Values: on / off
Declarative hibernation removes all cluster Pods while retaining PVCs (including replicas), saving CPU for batch workloads that don't need the database continuously . It deliberately avoids a switchover during shutdown to keep replicas synchronized.
Setting the annotation to off (or removing it) rehydrates the cluster by recreating all Pods.
Key distinction from fencing: Hibernation removes Pods; fencing keeps Pods running but stops the postmaster. Fencing is an emergency/debugging tool; hibernation is a cost-optimization feature for scheduled downtime.
The annotation constant is HibernationAnnotationName with values HibernationAnnotationValueOn/Off .
# Hibernate
kubectl annotate cluster <name> cnpg.io/hibernation=on --overwrite
# Rehydrate
kubectl annotate cluster <name> cnpg.io/hibernation=off --overwrite
Monitor status: kubectl cnpg status <cluster-name> or inspect the cnpg.io/hibernation condition on the Cluster.
Quick Reference#
| Annotation | Value | Scope | Effect | Use When |
|---|---|---|---|---|
cnpg.io/reconciliationLoop | disabled | Cluster | Operator skips all reconciliation | Manual cluster surgery without operator interference |
cnpg.io/fencedInstances | '["<name>"]' or '["*"]' | Cluster | Shuts down postmaster, pod stays; no failover triggered | Debugging a bad instance, preventing data writes |
cnpg.io/reconcilePodSpec | disabled | Cluster, Pooler | Prevents pod restarts on PodSpec drift | Temporary pod changes without rolling restarts |
cnpg.io/hibernation | on / off | Cluster | Removes all Pods (retains PVCs) / recreates Pods | Cost savings for batch workloads with scheduled downtime |
Primary source files:
| File | Purpose |
|---|---|
pkg/utils/labels_annotations.go | All annotation name constants |
pkg/utils/fencing.go | Fencing annotation read/write logic |
pkg/management/postgres/instance.go | Instance-level fencing state (IsFenced, SetFencing) |
internal/cmd/manager/instance/run/lifecycle/run.go | PostgreSQL startup gated on fencing check |
Official docs: