A Kubernetes operator that deletes namespaces without resources.
Warning
This operator is no longer being developed. Please use
remove-empty-ns instead — a
dedicated app designed to run as a CronJob (or ad-hoc from a shell / CI).
The initial intention here was to try out operator development in Python
(using kopf). In practice, the original use
case — removing empty namespaces — is a periodic cleanup task that fits an
app + CronJob better than a continuously reconciling operator: no
long-running controller to keep healthy, no finalizers to clean up, and it's
trivial to run once locally or in CI against any cluster.
remove-empty-ns is a redesign rather than a drop-in replacement — the config
model is different:
| This operator (kopf, Helm, long-running) | remove-empty-ns (script, Kustomize, CronJob/CLI) |
|---|---|
interval + initialDelay (reconcile timing) |
CronJob schedule + ageGreaterThan (min namespace age) |
protectedNamespaces (exact names) |
namespacesIgnored / namespacesConsidered (regex allow/deny) |
ignoredResouces (object-level ignore) |
resourcesIgnored / resourcesConsidered (object-level allow/deny) |
| — | apiResourcesIgnored / apiResourcesConsidered (API-kind allow/deny) |
| mark-then-delete always on (annotation) | doubleCheck opt-in (label; delete only if still empty next run) |
cleanupFinalizers (removes kopf finalizers on shutdown) |
not needed — no operator, no finalizers |
dryRun |
dryRun |
Operator iterates over all namespaced api-resources in every namespace. If there are no resources, it annotates namespace as a candidate for deletion. The namespace will be deleted after specified time interval if there will be no resources still.
So operator doesn't delete namespace instantly: first time it marks namespace and after interval operator deletes ns if it's still empty.
helm repo add remove-empty-ns-operator https://rgeraskin.github.io/remove-empty-ns-operator/
helm upgrade --install --create-namespace -n remove-empty-ns-operator remove-empty-ns-operator/remove-empty-ns-operatorSee settings in helm/values.yaml as example
interval: "18000" # 5h
initialDelay: "300" # 5m
ignoredResouces:
- apiGroup: ""
kind: ConfigMap
nameRegExp: kube-root-ca.crt
- apiGroup: ""
kind: ConfigMap
nameRegExp: werf-synchronization
- apiGroup: ""
kind: Secret
nameRegExp: default-token-\w+$
- apiGroup: ""
kind: ServiceAccount
nameRegExp: default
protectedNamespaces:
- default
- kube-public
- kube-system
cleanupFinalizers: true
dryRun: false-
interval- interval between namespaces check -
initialDelay- 'grace period' before new namespace will be checked -
ignoredResouces- namespace will be treated as empty if it contains only 'ignored resources' -
protectedNamespaces- these namespaces will not be deleated dispite of emptinessUsually there is no need to add kubernetes default namespaces (
default,kube-public, andkube-system) toprotectedNamespacesbecause they have some resources inside in the most cases. -
cleanupFinalizers- cleanup kopf finalizers from all namespaces during operator shutdown (motivation)If the finalizers cleanup takes longer than that in total (e.g. due to retries), the activity will not be finished in full, as the pod will be SIGKILL’ed by Kubernetes.
So adjust the value of
terminationGracePeriodSecondsif you have a lot of namespaces to cleanup. -
dryRun- dry run mode: don't delete namespaces, just log what would be deleted
- Prepare local dev env with mise:
mise install - Install pre-commit:
pre-commit install - Use
mise tasksfor common tasks - Use tilt for a development process, e.g.
tilt up - Tests:
mise run test