Skip to content

[Bug]: Non-ready pods are not cleared via alpha.cnpg.io/unrecoverable=true annotation #9947

Closed

Description

@d-uzlov

Is there an existing issue already for this bug?

  • I have searched for an existing issue, and could not find anything. I believe this is a new bug.

I have read the troubleshooting guide

  • I have read the troubleshooting guide and I think this is a new bug.

I am running a supported version of CloudNativePG

  • I have read the troubleshooting guide and I think this is a new bug.

Contact Details

No response

Version

1.28 (latest patch)

What version of Kubernetes are you using?

1.34

What is your Kubernetes environment?

Other

How did you install the operator?

Helm

What happened?

I have a postgres cluster with 2 nodes.
Both nodes containing postgres pods were shut down. After nodes booted back up, one pod works normally, but another one is permanently not ready. The working pod is a primary, the failed one is marked as replica.

I believe that this is a known issue. I have seen such state previously, and fixed it by manually deleting the PVC and the failed pod.
But in the CNPG 1.28 release notes I noticed a mention of alpha.cnpg.io/unrecoverable=true annotation and tried to use it.

However, the pod is not deleted, and CNPG operator logs don't show any attempts to do so, or errors.
Logs contain only repeated "Cannot extract Pod status" message, nothing else.

Cluster resource

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  annotations:
  creationTimestamp: "2025-04-02T10:40:40Z"
  generation: 8
  name: postgres
  namespace: cm-acme-dns
  resourceVersion: "1513722147"
  uid: 83deae02-da95-4055-801d-3939eabe8c0e
spec:
  affinity:
    enablePodAntiAffinity: true
    podAntiAffinityType: required
    topologyKey: kubernetes.io/hostname
  bootstrap:
    initdb:
      database: app
      encoding: UTF8
      localeCType: C
      localeCollate: C
      owner: app
  enablePDB: true
  enableSuperuserAccess: false
  failoverDelay: 0
  imageName: ghcr.io/cloudnative-pg/postgresql:17.4
  instances: 2
  logLevel: info
  managed:
    services:
      disabledDefaultServices:
      - ro
      - r
  maxSyncReplicas: 0
  minSyncReplicas: 0
  monitoring:
    customQueriesConfigMap:
    - key: queries
      name: cnpg-default-monitoring
    disableDefaultQueries: false
    enablePodMonitor: false
  plugins:
  - enabled: true
    isWALArchiver: true
    name: barman-cloud.cloudnative-pg.io
    parameters:
      barmanObjectName: s3-acme-dns-backup
  postgresGID: 26
  postgresUID: 26
  postgresql:
    parameters:
      archive_mode: "on"
      archive_timeout: 5min
      dynamic_shared_memory_type: posix
      full_page_writes: "on"
      hot_standby_feedback: "on"
      log_destination: csvlog
      log_directory: /controller/log
      log_filename: postgres
      log_rotation_age: "0"
      log_rotation_size: "0"
      log_truncate_on_rotation: "false"
      logging_collector: "on"
      max_parallel_workers: "32"
      max_replication_slots: "32"
      max_slot_wal_keep_size: 1GB
      max_worker_processes: "32"
      shared_memory_type: mmap
      shared_preload_libraries: ""
      ssl_max_protocol_version: TLSv1.3
      ssl_min_protocol_version: TLSv1.3
      wal_keep_size: 1GB
      wal_level: logical
      wal_log_hints: "on"
      wal_receiver_timeout: 5s
      wal_sender_timeout: 5s
    syncReplicaElectionConstraint:
      enabled: false
  primaryUpdateMethod: restart
  primaryUpdateStrategy: unsupervised
  probes:
    liveness:
      isolationCheck:
        connectionTimeout: 1000
        enabled: true
        requestTimeout: 1000
  replicationSlots:
    highAvailability:
      enabled: true
      slotPrefix: _cnpg_
    synchronizeReplicas:
      enabled: true
    updateInterval: 30
  resources:
    limits:
      memory: 150Mi
    requests:
      memory: 50Mi
  smartShutdownTimeout: 180
  startDelay: 3600
  stopDelay: 1800
  storage:
    resizeInUseVolumes: true
    size: 2Gi
    storageClass: tulip-nvmeof
  switchoverDelay: 3600

Relevant log output

{"level":"info","ts":"2026-02-10T06:26:38.675641965Z","logger":"setup","msg":"Starting CloudNativePG Operator","version":"1.28.1","build":{"Version":"1.28.1","Commit":"1ecc48340","Date":"2026-02-05"}}
...
{"level":"info","ts":"2026-02-10T06:26:42.955437713Z","msg":"Cannot extract Pod status","controller":"cluster","controllerGroup":"postgresql.cnpg.io","controllerKind":"Cluster","Cluster":{"name":"postgres","namespace":"cm-acme-dns"},"namespace":"cm-acme-dns","name":"postgres","reconcileID":"2e923631-d495-4188-84e6-f389616f654c","podName":"postgres-3","error":"error status code: 500, body: failed to connect to `user=postgres database=postgres`: /controller/run/.s.PGSQL.5432 (/controller/run): dial error: dial unix /controller/run/.s.PGSQL.5432: connect: no such file or directory\n"}

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. d-uzlov commented on Feb 10, 2026

    @d-uzlov
    Author
    kubectl -n cm-acme-dns describe pod postgres-3 (click me)
    $ kl -n cm-acme-dns describe pod postgres-3
    Name:             postgres-3
    Namespace:        cm-acme-dns
    Priority:         0
    Service Account:  postgres
    Node:             trixie-work-3/10.3.10.11
    Start Time:       Tue, 10 Feb 2026 06:16:10 +0000
    Labels:           app.kubernetes.io/component=database
                      app.kubernetes.io/instance=postgres
                      app.kubernetes.io/managed-by=cloudnative-pg
                      app.kubernetes.io/name=postgresql
                      app.kubernetes.io/version=17
                      cnpg.io/cluster=postgres
                      cnpg.io/instanceName=postgres-3
                      cnpg.io/instanceRole=replica
                      cnpg.io/podRole=instance
                      role=replica
    Annotations:      alpha.cnpg.io/unrecoverable: true
                      cnpg.io/nodeSerial: 3
                      cnpg.io/operatorVersion: 1.28.1
                      cnpg.io/podEnvHash: 767fdd8685
                      cnpg.io/podSpec:
                        {"volumes":[{"name":"pgdata","persistentVolumeClaim":{"claimName":"postgres-3"}},{"name":"scratch-data","emptyDir":{}},{"name":"shm","empt...
    Status:           Running
    SeccompProfile:   RuntimeDefault
    IP:               10.3.129.74
    IPs:
      IP:           10.3.129.74
    Controlled By:  Cluster/postgres
    Init Containers:
      bootstrap-controller:
        Container ID:    containerd://d5fef70198663394084f640933f6c016cff2e95eb05cb2ae35ad21cbcbeaf9d6
        Image:           ghcr.io/cloudnative-pg/cloudnative-pg:1.28.1
        Image ID:        ghcr.io/cloudnative-pg/cloudnative-pg@sha256:0edcdaefaa83d68822158cf4370d83d185c54db2b6beb7e0848aca16eafa6344
        Port:            <none>
        Host Port:       <none>
        SeccompProfile:  RuntimeDefault
        Command:
          /manager
          bootstrap
          /controller/manager
          --log-level=info
        State:          Terminated
          Reason:       Completed
          Exit Code:    0
          Started:      Tue, 10 Feb 2026 06:16:22 +0000
          Finished:     Tue, 10 Feb 2026 06:16:23 +0000
        Ready:          True
        Restart Count:  0
        Limits:
          memory:  150Mi
        Requests:
          memory:     50Mi
        Environment:  <none>
        Mounts:
          /controller from scratch-data (rw)
          /dev/shm from shm (rw)
          /run from scratch-data (rw)
          /var/lib/postgresql/data from pgdata (rw)
          /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-qzzdw (ro)
      plugin-barman-cloud:
        Container ID:    containerd://0137da452941b0c5bd27754b98a361d7469bf8d6dc9069edf8430726c3528fa7
        Image:           ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar:v0.11.0
        Image ID:        ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar@sha256:8e45fedbfaae916d7dff1160ea22648c2d21e6184479d3eb75e9ff0f3b23428a
        Port:            <none>
        Host Port:       <none>
        SeccompProfile:  RuntimeDefault
        Args:
          instance
        State:          Running
          Started:      Tue, 10 Feb 2026 06:16:24 +0000
        Ready:          True
        Restart Count:  0
        Startup:        exec [/manager healthcheck unix] delay=0s timeout=10s period=10s #success=1 #failure=10
        Environment:
          PGDATA:               /var/lib/postgresql/data/pgdata
          POD_NAME:             postgres-3
          NAMESPACE:            cm-acme-dns
          CLUSTER_NAME:         postgres
          PSQL_HISTORY:         /controller/tmp/.psql_history
          PGPORT:               5432
          PGHOST:               /controller/run
          TMPDIR:               /controller/tmp
          SPOOL_DIRECTORY:      /controller/wal-restore-spool
          CUSTOM_CNPG_GROUP:    postgresql.cnpg.io
          CUSTOM_CNPG_VERSION:  v1
        Mounts:
          /controller from scratch-data (rw)
          /dev/shm from shm (rw)
          /plugins from plugins (rw)
          /var/lib/postgresql/data from pgdata (rw)
          /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-qzzdw (ro)
    Containers:
      postgres:
        Container ID:    containerd://74885bb17fd3b4ff879f835e3c6968f779406143d241a0d788d18b1e983f90fe
        Image:           ghcr.io/cloudnative-pg/postgresql:17.4
        Image ID:        ghcr.io/cloudnative-pg/postgresql@sha256:ace32caf538170ce87b141d340966d92955c948a0024e52523ae75a3a1a3c685
        Ports:           5432/TCP (postgresql), 9187/TCP (metrics), 8000/TCP (status)
        Host Ports:      0/TCP (postgresql), 0/TCP (metrics), 0/TCP (status)
        SeccompProfile:  RuntimeDefault
        Command:
          /controller/manager
          instance
          run
          --status-port-tls
          --log-level=info
        State:          Running
          Started:      Tue, 10 Feb 2026 07:16:31 +0000
        Last State:     Terminated
          Reason:       Completed
          Exit Code:    0
          Started:      Tue, 10 Feb 2026 06:16:31 +0000
          Finished:     Tue, 10 Feb 2026 07:16:31 +0000
        Ready:          False
        Restart Count:  1
        Limits:
          memory:  150Mi
        Requests:
          memory:   50Mi
        Liveness:   http-get https://:8000/healthz delay=0s timeout=5s period=10s #success=1 #failure=3
        Readiness:  http-get https://:8000/readyz delay=0s timeout=5s period=10s #success=1 #failure=3
        Startup:    http-get https://:8000/startupz delay=0s timeout=5s period=10s #success=1 #failure=360
        Environment:
          PGDATA:        /var/lib/postgresql/data/pgdata
          POD_NAME:      postgres-3
          NAMESPACE:     cm-acme-dns
          CLUSTER_NAME:  postgres
          PSQL_HISTORY:  /controller/tmp/.psql_history
          PGPORT:        5432
          PGHOST:        /controller/run
          TMPDIR:        /controller/tmp
        Mounts:
          /controller from scratch-data (rw)
          /dev/shm from shm (rw)
          /plugins from plugins (rw)
          /run from scratch-data (rw)
          /var/lib/postgresql/data from pgdata (rw)
          /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-qzzdw (ro)
    Readiness Gates:
      Type             Status
      KruisePodReady   True
    Conditions:
      Type                        Status
      KruisePodReady              True
      PodReadyToStartContainers   True
      Initialized                 True
      Ready                       False
      ContainersReady             False
      PodScheduled                True
    Volumes:
      pgdata:
        Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
        ClaimName:  postgres-3
        ReadOnly:   false
      scratch-data:
        Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
        Medium:     
        SizeLimit:  <unset>
      shm:
        Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
        Medium:     Memory
        SizeLimit:  <unset>
      plugins:
        Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
        Medium:     
        SizeLimit:  <unset>
      kube-api-access-qzzdw:
        Type:                    Projected (a volume that contains injected data from multiple sources)
        TokenExpirationSeconds:  3607
        ConfigMapName:           kube-root-ca.crt
        Optional:                false
        DownwardAPI:             true
    QoS Class:                   Burstable
    Node-Selectors:              <none>
    Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
    Events:
      Type     Reason     Age                    From     Message
      ----     ------     ----                   ----     -------
      Normal   Pulled     12m (x2 over 72m)      kubelet  Container image "ghcr.io/cloudnative-pg/postgresql:17.4" already present on machine
      Normal   Created    12m (x2 over 72m)      kubelet  Created container: postgres
      Normal   Started    12m (x2 over 72m)      kubelet  Started container postgres
      Normal   Killing    12m                    kubelet  Container postgres failed startup probe, will be restarted
      Warning  Unhealthy  2m34s (x421 over 72m)  kubelet  Startup probe failed: HTTP probe failed with statuscode: 500
    
  2. dosubot commented on Feb 10, 2026

    @dosubot

    The operator is supposed to delete pods annotated with alpha.cnpg.io/unrecoverable=true (as long as they're not the current or target primary), but in practice, pods like yours can get stuck and not be deleted automatically. This happens when the operator can't extract the pod's status—often because the main Postgres container is failing its startup probe and restarting, while a sidecar (like barman-cloud) is still running. In this state, the operator logs repeated "Cannot extract Pod status" messages and doesn't proceed with deletion, even though the annotation is present and valid (operator code, user reports).

    This is a known lifecycle handling gap. The recommended workaround is to manually delete the stuck pod and its PVC. The operator will then recreate the pod and resync the replica as expected. This is a safe and common recovery step in current CNPG releases (docs, discussion). Future releases may improve this behavior, but for now, manual intervention is sometimes necessary.

    To reply, just mention @dosu.


    Share context across your team and agents. Try Dosu.

    Leave Feedback Learn about cloudnative-pg with Dosu Add Dosu to your team

  3. added a commit that references this issue on Feb 12, 2026
    ec7f638
  4. added a commit that references this issue on Feb 27, 2026
    33870d9
  5. added a commit that references this issue on Feb 27, 2026
    09b91e5
  6. added a commit that references this issue on Feb 27, 2026
    ac652f7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

triagePending triage

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions