PodMonitor Management in CloudNativePG#
CloudNativePG manages PodMonitor resources (from the Prometheus Operator API) for both Cluster and Pooler objects. The operator creates, reconciles, and deletes these resources automatically when spec.monitoring.enablePodMonitor is set β though that field is now deprecated (see Deprecation below).
The operator requires get;create;list;watch;delete;patch RBAC on monitoring.coreos.com/podmonitors .
Core Reconciliation: createOrPatchPodMonitor#
Both Cluster and Pooler share a single generic reconciliation function, createOrPatchPodMonitor, which accepts any type implementing the podMonitorManager interface :
type podMonitorManager interface {
IsPodMonitorEnabled() bool
BuildPodMonitor() *monitoringv1.PodMonitor
}
The function's four-case reconciliation logic :
IsPodMonitorEnabled() | PodMonitor exists? | Action |
|---|---|---|
false | No | No-op |
false | Yes | Delete only if owned by the cluster/pooler |
true | No | Create |
true | Yes | Patch (merge metadata, update spec; skip if no diff) |
CRD guard: If the PodMonitor CRD is not installed in the cluster, the function logs a warning and returns early instead of erroring . This allows clusters to operate without the Prometheus Operator installed.
Ownership check on delete: Before deleting an existing PodMonitor when monitoring is disabled, the function verifies ownership with IsOwnedByCluster . This prevents accidental deletion of manually-created PodMonitors that share the same name/namespace β a bug that was fixed in PR #9340.
Metadata merging on update: When patching, the spec is fully replaced but labels/annotations are merged (not overwritten), preserving any user-managed metadata .
Cluster PodMonitor#
Entry point: createPostgresClusterObjects calls createOrPatchPodMonitor with a ClusterPodMonitorManager on every reconcile pass .
Spec builder: pkg/specs/podmonitor.go defines ClusterPodMonitorManager.BuildPodMonitor(), which:
- Names the PodMonitor after the Cluster
- Sets ownership via
cluster.SetInheritedDataAndOwnership - Selects pods matching
cnpg.io/cluster: <name>+role: instance - Optionally configures HTTPS with the server CA secret when
IsMetricsTLSEnabled()is true
IsPodMonitorEnabled() delegates to cluster.IsPodMonitorEnabled() .
Pooler PodMonitor#
Entry point: updateOwnedObjects (the Pooler reconciler's main owned-resource sync function) calls createOrPatchPodMonitor last, after reconciling service accounts, RBAC, deployment, and service . This runs even when the Pooler's image cannot be resolved (i.e., Phase=Failed), so monitoring drift is corrected independently of deployment state .
Spec builder: pkg/specs/pgbouncer/podmonitor.go defines PoolerPodMonitorManager.BuildPodMonitor(), which:
- Names the PodMonitor after the Pooler
- Sets ownership via
utils.SetAsOwnedBy - Selects pods matching
cnpg.io/pgbouncer: <name>+role: pooler - When
IsMetricsTLSEnabled()is true, configures HTTPS withInsecureSkipVerify: true(because Prometheus scrapes pods by IP, so the cert's SANs won't match) β users needing strict verification must disableenablePodMonitorand manage their own PodMonitor
IsPodMonitorEnabled() checks pooler.Spec.Monitoring.EnablePodMonitor .
Deprecation of spec.monitoring.enablePodMonitor#
spec.monitoring.enablePodMonitor is deprecated for both Cluster and Pooler as of PR #8753 (merged October 2025). The same PR also deprecated the companion relabeling fields:
spec.monitoring.podMonitorMetricRelabelingsspec.monitoring.podMonitorRelabelings
Migration path: Users should remove enablePodMonitor: true and create a PodMonitor resource manually. The admission webhook now emits warnings for both Cluster and Pooler when these deprecated fields are set . The Helm chart's podmonitor-pooler.yaml follows this pattern β it creates the PodMonitor directly rather than delegating to the operator via enablePodMonitor .
The operator continues to honor the deprecated fields during the deprecation period, reading them with //nolint:staticcheck annotations .
Key Source Files#
| File | Purpose |
|---|---|
internal/controller/cluster_create.go (lines 998β1078) | createOrPatchPodMonitor β shared reconciliation logic |
pkg/specs/podmonitor.go | ClusterPodMonitorManager β builds Cluster PodMonitor spec |
pkg/specs/pgbouncer/podmonitor.go | PoolerPodMonitorManager β builds Pooler PodMonitor spec |
internal/controller/pooler_update.go (lines 63β68) | Pooler entry point into createOrPatchPodMonitor |
internal/controller/cluster_create.go (line 100) | Cluster entry point into createOrPatchPodMonitor |