Skip to content
SRE & DevOpsFix Published Updated 3 min readViews unavailable

Fixing a Kubernetes Namespace Stuck in Terminating Without Hiding the Root Cause

A safe namespace-termination investigation covering discovery failures, remaining objects, finalizers, APIService health, and last-resort finalization.

A namespace remains in Terminating because Kubernetes has not completed the conditions recorded on it. Force-removing the namespace finalizer can make the row disappear while leaving namespaced objects in backing stores or extension APIs. The safe fix is to identify which condition is unresolved, repair that dependency, and let the namespace controller finish normally.

Read the status before editing anything

kubectl get namespace demo -o yaml

Inspect .status.conditions. Recent Kubernetes versions record whether resource discovery succeeded, group versions were parsed, content was deleted, and content-preserving finalizers remain. Messages often name an unavailable API group or count remaining resources and finalizers.

Also record the deletion timestamp, namespace UID, cluster version, and recent controller-manager logs. Do not remove fields yet; this snapshot is the evidence needed to understand whether the blocker is discovery, deletion, or finalization.

Repair broken API discovery first

The namespace controller discovers every namespaced resource so it can delete all content. An aggregated APIService whose backing service is unavailable can prevent complete discovery even when that API has no objects in the namespace.

kubectl get apiservice
kubectl describe apiservice v1beta1.example.io

Check the extension API’s Service, Endpoints or EndpointSlices, TLS configuration, CA bundle, and Pods. If the API was intentionally retired, remove its registration through the operator or uninstall procedure that owns it. Deleting an APIService blindly can damage an active controller just as surely as leaving a dead one can block termination.

Enumerate remaining namespaced objects

After discovery is healthy, list every namespaced resource type rather than only common workloads:

kubectl api-resources --verbs=list --namespaced -o name | \
  xargs -n 1 kubectl get --ignore-not-found -n demo

Run this from a controlled administrative environment and review errors; xargs output alone can hide which resource type failed. Custom resources, webhook-managed objects, Jobs, leases, and operator internals are common leftovers.

For each object, inspect metadata.finalizers, deletion timestamp, owner references, and the controller responsible for that finalizer. A finalizer is not garbage text: it is a promise that a controller must complete external cleanup before deletion.

Restore or deliberately replace the finalizer controller

If an operator was removed before its custom resources, reinstall the compatible controller or use its documented cleanup tool. Check its permissions, webhook availability, credentials, and external dependencies. A finalizer can remain because cleanup is genuinely failing—for example, a cloud API is unreachable or a credential expired.

Only remove an object’s finalizer after proving what cleanup it represents, performing that cleanup manually when required, and recording the exception. Patch the individual object rather than jumping directly to the namespace finalizer; this gives the namespace controller a chance to verify an empty namespace.

Treat namespace finalization as the last resort

The namespace finalize subresource can be used to clear spec.finalizers, but doing so can create orphaned objects that become difficult to address because their namespace no longer exists. Recreating a namespace with the same name does not recreate its old UID or guarantee access to orphaned extension data.

If business impact requires the last resort, take an etcd/control-plane backup through the supported platform mechanism, export the namespace and remaining objects, obtain approval from the owners of every finalizer, and record the extension APIs involved. Use the API server’s finalize subresource for the exact namespace object; do not mutate etcd directly.

Verify that cleanup really completed

After termination, confirm the namespace is absent, extension API health is green, operator backlogs are empty, external cloud resources were removed or deliberately retained, and no controller logs continue referencing the old UID. Search monitoring and billing systems for orphaned resources.

Prevent recurrence by ordering operator uninstallations: delete managed custom resources first, wait for finalizers, then remove the controller, CRDs, and API registrations. Alert on namespaces or objects whose deletion age exceeds an expected window. Termination latency is operational evidence, not a cosmetic state to erase.

Related:

Sources:

Comments