✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Finalizer Extension Management

Kubernetes Finalizer Extension Management ensures clean resource deletion by extending finalizers to control lifecycle operations across Kubernetes objects.

Kubernetes Finalizer Extension Management is the practice of using the metadata.finalizers mechanism as a general-purpose extension point across any Kubernetes resource type, not only custom resources, to inject external cleanup logic into an object's deletion path, along with the naming conventions, ordering semantics, and troubleshooting practices required to use finalizers without leaving objects permanently stuck.


Finalizers as a Cross-Resource Extension Mechanism

Built-In Uses of Finalizers

Finalizers are not exclusive to custom resources; core Kubernetes uses them extensively, including the kubernetes.io/pv-protection finalizer preventing a PersistentVolume from being deleted while still bound, and the namespace controller's own finalizer mechanism that blocks a Namespace from fully terminating until every resource within it has been deleted.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: data-volume
  finalizers:
    - kubernetes.io/pv-protection
kubectl get namespace decommissioned-team -o jsonpath='{.spec.finalizers}'

The Finalizer Set Is Unordered

metadata.finalizers is a simple string array with no ordering guarantee enforced by the API server; if a resource's cleanup requires a specific sequence (external DNS deregistration before credential revocation, for instance), that ordering must be enforced by the controller's own deletion-handling logic, not assumed from array position.

metadata:
  finalizers:
    - databases.example.com/deregister-dns
    - databases.example.com/revoke-credentials

Naming and Ownership Conventions

Domain-Qualified Finalizer Names

finalizers:
  - databases.example.com/cleanup-backups

Following the same domain-qualification convention used for CRD groups, a finalizer name should be prefixed with a domain the adding controller owns, both to avoid collision with an unrelated controller's finalizer of the same short name, and to make clear from the name alone which controller is responsible for removing it.

Only the Adding Controller Should Remove Its Own Finalizer

A controller must only remove finalizer entries it recognizes as its own; removing an unrecognized finalizer entry (belonging to a different controller) can allow deletion to proceed before that other controller has performed its required cleanup, defeating the purpose of the mechanism entirely.

controllerutil.RemoveFinalizer(cluster, "databases.example.com/cleanup-backups")
// never remove finalizers with a different domain prefix
Object Deletable finalizers =

Adding Finalizers via Mutating Admission

Injecting a Finalizer at Creation Time

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: postgrescluster-finalizer-injector
webhooks:
  - name: finalizer.databases.example.com
    rules:
      - apiGroups: ["databases.example.com"]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["postgresclusters"]

Some controllers add their finalizer through a mutating webhook at creation time rather than in the first reconcile pass, guaranteeing the finalizer is present from the object's very first version and eliminating a narrow race window in which an object could theoretically be deleted before the controller's own reconcile loop has had a chance to add its finalizer.


Troubleshooting Stuck Finalizers

Diagnosing a Terminating Object

kubectl get postgrescluster orders-db -o jsonpath='{.metadata.finalizers}'
kubectl describe postgrescluster orders-db

An object stuck with a non-nil deletionTimestamp and a non-empty finalizers list for longer than expected almost always indicates the owning controller's cleanup logic is failing repeatedly, is not running at all (crashed, misconfigured RBAC, or simply not deployed), or has a bug that never reaches the finalizer-removal code path; checking controller logs and events for that specific object name is the standard first diagnostic step.

Manual Finalizer Removal as a Last Resort

kubectl patch postgrescluster orders-db --type=merge -p '{"metadata":{"finalizers":[]}}'

Forcibly clearing finalizers on a stuck object bypasses whatever external cleanup the finalizer existed to guarantee, permanently orphaning any external state (cloud snapshots, DNS records, credentials) that cleanup would have removed; this should only be used as a deliberate, understood last resort after confirming the underlying controller cannot be fixed or restarted to complete its cleanup normally, never as a routine unblocking technique.


Finalizers and Cascading Deletion Interaction

Combining with blockOwnerDeletion

ownerReferences:
  - controller: true
    blockOwnerDeletion: true

When a dependent object both carries a finalizer and is referenced with blockOwnerDeletion: true by its owner, a foreground cascading delete of the owner will not complete until the dependent's own finalizer has been fully processed and removed, meaning finalizer logic on dependents can extend how long an entire owner-and-dependents deletion sequence takes to finish, which should be accounted for in any automation with a fixed deletion timeout.


Relationship to Custom Resource Lifecycle and Ownership Management

Finalizer extension management is the specific mechanism instantiating the finalization phase of the custom resource lifecycle, and it interacts directly with ownership management's cascading deletion policies: where ownership management determines which dependent objects are automatically garbage-collected, finalizers are the tool for anything the garbage collector cannot know how to clean up on its own, namely state that exists outside the Kubernetes API entirely.

DELETE request Finalizer cleanup Removed