skip to content

In Kubernetes, what happens when you run kubectl delete namespace, and why does the namespace show Terminating before it disappears?

level: juniorimportance: should knowfreq 58%

answer

  1. two phases, not one step
  2. a marker only one controller clears
  3. spec.finalizers, not metadata
  4. pods go before everything else
  5. admission blocks new content

basics

~10 s

Deleting a Kubernetes namespace only marks it Terminating. The namespace controller deletes every object inside, pods first, then removes the kubernetes finalizer, and only then does the API server remove the namespace.

solid answer

~40 s

`kubectl delete namespace` makes the API server set `deletionTimestamp` and switch `status.phase` from `Active` to `Terminating`; the Namespace object stays. Every namespace carries the `kubernetes` entry in `spec.finalizers`, owned by the namespace controller in `kube-controller-manager`. That controller discovers every namespaced resource type, deletes all pods first, then everything else, and records progress in `status.conditions`. When nothing is left it removes its finalizer entry, and with `spec.finalizers` empty the API server deletes the namespace. While it is `Terminating`, the `NamespaceLifecycle` admission plugin rejects new objects in it. The purge takes as long as its slowest object: an inference server with a 19-minute grace period keeps it `Terminating` for about 19 minutes.

code

bash · 3 lines
bash
kubectl delete namespace recsys-inference --wait=false
kubectl get namespace recsys-inference -o jsonpath='{.status.phase}{"\n"}{.spec.finalizers}{"\n"}'
kubectl wait --for=delete namespace/recsys-inference --timeout=25m

go deeper

for a junior

Recall the two phases, Active and Terminating, and that deleting a namespace deletes everything inside it before the namespace itself goes away.

for a middle

Explain the kubernetes entry in spec.finalizers, which controller owns it, and the pods-first order in which the purge deletes content.

for a senior

Show you can tell a slow but healthy purge (long grace periods) from a stuck one, and design pipelines that wait for deletion rather than racing it.

for a principal

Consider namespace deletion as a tenant offboarding step: how long it may take, what it removes, and what must be cleaned outside the cluster.

## Two phases: `Active` and `Terminating` A Kubernetes **Namespace** reports its lifecycle in `status.phase`, which has exactly two values: - **`Active`** — the namespace is in normal use and new objects can be created in it. - **`Terminating`** — someone has asked for the namespace to be deleted and the cluster is cleaning it out. When you run `kubectl delete namespace recsys-inference`, the API server does **not** remove the Namespace object. On that first delete request it sets `metadata.deletionTimestamp` and flips `status.phase` to `Terminating` in the same write. The object stays readable, and `kubectl get namespace` keeps listing it until the cleanup is finished. By default `kubectl delete` also waits (`--wait=true`) for the object to be gone, which is why the command can sit there for minutes. ## The `kubernetes` entry in `spec.finalizers` A **finalizer** is a marker that says "some component still has work to do before this object may be removed". Namespaces carry a special one. When a Namespace is created, the API server adds the value `kubernetes` to the namespace's own `spec.finalizers` list (a Namespace-only field, separate from the generic `metadata.finalizers` list every object has). The rule the API server enforces is simple: a namespace with a `deletionTimestamp` is only removed from storage once `spec.finalizers` is **empty** (and, like any object, once its `metadata.finalizers` is empty too). The component that owns the `kubernetes` entry is the **namespace controller**, one of the controllers inside `kube-controller-manager`. Until it removes that entry, the namespace cannot go away. ## What the namespace controller does For every namespace that has a `deletionTimestamp`, the namespace controller runs a purge loop: 1. **Discover** every namespaced resource type the API server serves, including custom resources and types served by aggregated APIs. 2. **Delete pods first.** It deletes every Pod in the namespace and waits until none remain before touching any other type, so workloads do not lose their Secrets, ConfigMaps or Services while they are still shutting down. 3. **Delete everything else** — Deployments, Services, ConfigMaps, Secrets, PersistentVolumeClaims, custom resources — type by type. 4. **Record progress** in the namespace's `status.conditions` (for example `NamespaceContentRemaining` when objects are still present). 5. **Retry** until every type reports zero objects left. 6. **Finalize**: remove `kubernetes` from `spec.finalizers`. With the list empty, the API server deletes the Namespace object itself. The deletions in steps 2 and 3 are ordinary deletes, so each object still goes through its own graceful shutdown and its own finalizers before it really disappears. ## Why `Terminating` can last a while The namespace can only finish as fast as its slowest object. Suppose `recsys-inference` runs a recommendation-model inference server whose pods set `terminationGracePeriodSeconds: 1140` so that in-flight batch requests can drain. On a 3-node kubeadm cluster, deleting the namespace leaves it in `Terminating` for about **19 minutes**: the pods honour their grace period, and because pods go first, the namespace's other objects are not even deleted until those pods are gone. That is normal behaviour, not a hang. ## What you can and cannot do while it terminates | Action in a `Terminating` namespace | Result | |---|---| | Create a new object | Rejected by the `NamespaceLifecycle` admission plugin: "unable to create new content in namespace ... because it is being terminated" | | Read existing objects | Allowed; they are still being removed | | Update or delete existing objects | Allowed | | Delete the namespace again | Returns the namespace object without starting anything new | | Create a namespace with the same name | Fails until the old one is fully gone | A CI pipeline that deletes and immediately recreates a namespace of the same name therefore fails intermittently. It should wait for the old namespace to disappear (for example with `kubectl wait --for=delete namespace/recsys-inference --timeout=25m`) or use a fresh name. ## Namespaces you cannot delete The same `NamespaceLifecycle` admission plugin refuses to delete `default`, `kube-system` and `kube-public` at all ("this namespace may not be deleted"). Every other namespace, including ones an add-on created, can be deleted and will be purged. ## Version note Deleting pods before other content is the `OrderedNamespaceDeletion` behaviour. It has been GA and locked on since Kubernetes 1.34, and by 1.37 its feature gate has been removed, so the ordering is always on. On older clusters the controller deleted all types without that ordering.

  • Why does the namespace controller delete pods before the other objects in the namespace?
    Pods may still be running during their grace period and can depend on the namespace's Secrets, ConfigMaps, Services or ServiceAccount tokens. Deleting those first could break a clean shutdown. With this ordering, GA since Kubernetes 1.34, the controller deletes pods, waits until none remain, and only then deletes the other types.
  • Your pipeline deletes a Kubernetes namespace and immediately recreates one with the same name. Why does that fail sometimes?
    The old namespace still exists in `Terminating` until its purge finishes, so creating a namespace with that name is rejected as already existing. The pipeline should wait for the old object to disappear, for example with `kubectl wait --for=delete namespace/<name>`, or use a unique name per run.

It is like closing a shop: the sign flips to 'closing' first, staff clear every shelf, and only after the last item leaves is the lease cancelled.

saying these in an interview costs you the question

  • kubectl delete namespace removes the namespace object immediately
  • Objects inside a deleted namespace keep running because they are separate objects
  • The kubernetes finalizer lives in the namespace's metadata.finalizers list
  • You can keep creating pods in a Terminating namespace until it is gone
  • A namespace in Terminating for several minutes is always broken
  • kube-system can be deleted like any other namespace