CloudNativePG Job Management#
Overview#
CloudNativePG uses short-lived Kubernetes batch/v1 Jobs to execute every lifecycle operation that initializes PostgreSQL data directories before an instance Pod starts. Each Job runs the operator manager binary (/controller/manager) inside the PostgreSQL image and exits on completion. Instance Pods are only created after their Jobs finish and mark their PVCs as ready.
Job Types#
Six job roles are defined in pkg/specs/jobs.go:
| Role label | Triggered by |
|---|---|
initdb | spec.bootstrap.initdb (new cluster) |
import | spec.bootstrap.initdb.import (logical import) |
full-recovery | spec.bootstrap.recovery (object-store backup) |
snapshot-recovery | spec.bootstrap.recovery with VolumeSnapshot source |
pgbasebackup | spec.bootstrap.pgBaseBackup |
join | Replica scale-up (streaming replication from primary) |
Each role maps to a public factory function in pkg/specs/jobs.go:
CreatePrimaryJobViaInitdb— buildsinitdbflags (encoding, locale, data checksums, WAL segment size, post-init SQL) and delegates toCreatePrimaryJobCreatePrimaryJobViaRecovery— issuesinstance restoreCreatePrimaryJobViaRestoreSnapshot— issuesinstance restoresnapshotwith backup-label/tablespacemap annotations from the VolumeSnapshotCreatePrimaryJobViaPgBaseBackup— issuesinstance pgbasebackupJoinReplicaInstance— issuesinstance join --parent-node <rw-service>RestoreReplicaInstance— issuesinstance restoresnapshot --immediatefor replica bootstrap from a VolumeSnapshot
Job Pod Specification#
All six roles are assembled through the central CreatePrimaryJob function. The resulting batchv1.Job pod template deliberately mirrors the steady-state instance Pod structure to keep container behavior consistent:
- Init container (
bootstrap-controller): runs the operator image withmanager bootstrap /controller/managerto copy the operator binary into the shared volume before the main container starts . The identical container appears in instance Pods viacreateClusterPodSpec. - Main container: named after the job role, runs the PostgreSQL image with the appropriate
instance <subcommand>. RestartPolicy: Never— failed Jobs leave their Pod inFailedstate so logs are preserved for debugging.- Volumes and mounts:
createPostgresVolumesandCreatePostgresVolumeMountsare called from the same helpers as instance Pods . - Scheduling:
SchedulerName,Affinity,Tolerations,NodeSelector,TopologySpreadConstraints,PriorityClassName, andServiceAccountNameare carried fromcluster.Spec. - Security context:
GetSecurityContextandGetPodSecurityContextare the same functions used by instance Pods —runAsNonRoot: true,readOnlyRootFilesystem: true,allowPrivilegeEscalation: false, all capabilities dropped . - AppArmor: propagated from cluster annotations if present .
- Subdomain: set to the cluster's "any" service when
CreateAnyServiceis enabled .
For initdb jobs that create an application database, the APP_USERNAME env var is injected from the application secret to enforce that the secret exists before cluster initialization proceeds .
Resource Allocation#
Both the init container and the main container in Jobs set Resources directly from cluster.Spec.Resources — the same field used by the steady-state postgres container in instance Pods :
- Main container:
Resources: cluster.Spec.Resources - Init container (
createBootstrapContainer):Resources: cluster.Spec.Resources
There is no separate resource field for Jobs. Bootstrap jobs run with exactly the same CPU/memory constraints as steady-state instance Pods. See CloudNativePG Bootstrap Job Resources for known limitations and workarounds (e.g., namespace-scoped LimitRange injection) when environments require explicit resource declarations on every pod.
Controller Flow#
Job creation is driven from internal/controller/cluster_create.go:
- Primary bootstrap —
createPrimaryInstancechecks cluster initialization state, resolves the recovery source (backup or VolumeSnapshot), generates a node serial, creates PVCs, then callsbuildPrimaryInstanceJobwhich dispatches to the appropriate factory based oncluster.Spec.Bootstrap. - Replica join —
joinReplicaInstancecallsJoinReplicaInstance; if a VolumeSnapshot backup is available for the replica, it callsRestoreReplicaInstanceinstead. - Missing bootstrap Job recovery —
ensureInstancesAreCreateddetects PVCs not yet markedreadyand callsensurePrimaryBootstrapJobto recreate a missing primary bootstrap Job (guards against optimistic-lock race conditions where a Job is lost between PVC creation and Job creation).
After building the Job, the controller:
- Sets the Cluster as the Job owner via
ctrl.SetControllerReference - Propagates inherited annotations and labels to both the Job and its pod template via
inheritJobMetadata - Adopts an already-existing Job (idempotency for stale-cache scenarios) via
ensureJobAdoptable
Naming and Labels#
Job names follow the pattern <cluster-name>-<serial>-<role> (e.g., mycluster-1-initdb). Both the Job and its pod template share the same label set :
| Label | Value |
|---|---|
cnpg.io/instanceName | <cluster-name>-<serial> |
cnpg.io/cluster | cluster name |
cnpg.io/jobRole | role string (initdb, join, etc.) |
app.kubernetes.io/name | cloudnative-pg |
app.kubernetes.io/managed-by | cnpg-controller-manager |
Key Source Files#
| File | Purpose |
|---|---|
pkg/specs/jobs.go | All job factory functions and the central CreatePrimaryJob assembler |
pkg/specs/containers.go | createBootstrapContainer, GetSecurityContext |
pkg/specs/pods.go | createClusterPodSpec, createPostgresContainers, shared env/volume helpers |
internal/controller/cluster_create.go | Controller: createPrimaryInstance, joinReplicaInstance, buildPrimaryInstanceJob, inheritJobMetadata |