A Kubernetes controller that automatically cleans up old Pods based on their age.
Kubernetes clusters, especially in development or CI/CD environments, can accumulate many completed or failed Pods over time (e.g., from Job resources). These leftover Pods consume resources and clutter monitoring dashboards. pod-housekeeper provides an automated way to identify and remove these old Pods based on configurable time thresholds.
- Two-Stage Deletion: Pods are first marked (annotated) after a configurable duration, then deleted after a second configurable duration.
- Namespace Exclusion: Specify namespaces where the housekeeper should not operate.
- Owner Limits: Limit the number of Pods marked for deletion simultaneously per owner (e.g., per
ReplicaSet,StatefulSet,Job). Kind-specific limits can also be set. - Annotation Exclusion: Pods with the
pod-housekeeper/exclude: "true"annotation are ignored. - Self-Exclusion: Automatically excludes its own Pod if deployed within the cluster (requires Downward API).
- Configurable: Settings managed via environment variables or a configuration file (using Viper).
- Metrics: Exposes Prometheus metrics for monitoring marked and deleted Pods.
pod-housekeeperwatches Pods across the cluster (respecting namespace exclusions).- When a Pod's age (
time.Now() - pod.Status.StartTime) exceedsPOD_HOUSEKEEPER_MARK_DURATION, the controller checks owner limits and exclusion rules. - If eligible, the Pod is annotated with
pod-housekeeper/marked-for-deletion: <timestamp>. - The controller continues watching marked Pods.
- When the time since the Pod was marked exceeds (
POD_HOUSEKEEPER_DELETE_DURATION - POD_HOUSEKEEPER_MARK_DURATION), the controller deletes the Pod.
Configuration is managed by Viper, prioritizing:
- Command-line flags (currently only
--max-concurrent-reconciles) - Environment variables (prefixed with
POD_HOUSEKEEPER_) - Configuration file (specified via
--configflag) - Default values
| Environment Variable | Flag / Config Key | Default | Description |
|---|---|---|---|
POD_HOUSEKEEPER_MARK_DURATION |
markDuration |
24h |
Duration after which a Pod is eligible to be marked for deletion. |
POD_HOUSEKEEPER_DELETE_DURATION |
deleteDuration |
48h |
Total duration after which a Pod should be deleted. Must be > markDuration. Deletion happens deleteDuration - markDuration after marking. |
POD_HOUSEKEEPER_EXCLUDED_NAMESPACES |
excludedNamespaces |
"" |
Comma-separated list of namespaces to ignore (e.g., "kube-system,monitoring"). |
POD_HOUSEKEEPER_MAX_MARKED_PER_OWNER |
maxMarkedPerOwner |
1 |
Default maximum number of Pods marked simultaneously per controller owner (e.g., ReplicaSet UID). Minimum 1. |
POD_HOUSEKEEPER_MAX_CONCURRENT_RECONCILES |
maxConcurrentReconciles / --max-concurrent-reconciles |
2 |
Maximum number of concurrent reconcile loops. Minimum 1. Flag overrides env/config. |
POD_HOUSEKEEPER_EXCLUDE_SELF |
excludeSelf |
true (if POD_NAME/POD_NAMESPACE env vars are set), false otherwise |
Whether the controller should ignore its own Pod. Requires Downward API to set POD_NAME and POD_NAMESPACE. |
POD_HOUSEKEEPER_CHECK_EXCLUDE_ANNOTATION |
checkExcludeAnnotation |
false |
If true, checks for the pod-housekeeper/exclude: "true" annotation on Pods before processing them. |
POD_HOUSEKEEPER_MAX_MARKED_PER_OWNER_BY_KIND |
maxMarkedPerOwnerByKind |
{} (empty map) |
A map in the config file (not env var) to specify owner limits per Kind (e.g., ReplicaSet: 5, Job: 1). Uses default if Kind not listed. |
Example Configuration File (config.yaml)
markDuration: 12h
deleteDuration: 24h
excludedNamespaces: "kube-system,logging"
maxMarkedPerOwner: 2
checkExcludeAnnotation: true
excludeSelf: true # Explicitly set if desired
maxMarkedPerOwnerByKind:
ReplicaSet: 3
Job: 1Pass this file using the --config flag: /pod-housekeeper --config config.yaml
- Kubernetes Cluster (>= 1.19 recommended for controller-runtime features)
kubectlinstalled
The included Dockerfile builds the controller. The GitHub Actions workflow in .github/workflows/build-and-push.yml automatically builds and pushes the image to GHCR (ghcr.io/itspooya/pod-housekeeper) on pushes to main or version tags (v*.*.*).
Build manually:
docker build -t ghcr.io/itspooya/pod-housekeeper:latest .
# docker push ghcr.io/itspooya/pod-housekeeper:latestReplace itspooya with your GitHub username/organization.
Standard Kubernetes manifests are expected to be in the config/ directory.
- Update Image: Ensure the
imagefield inconfig/deployment.yamlpoints to your built image (e.g.,ghcr.io/itspooya/pod-housekeeper:latest). - Apply Manifests:
You can apply all manifests in theconfig/directory at once:Or individually:kubectl apply -f config/
kubectl apply -f config/namespace.yaml kubectl apply -f config/rbac.yaml kubectl apply -f config/deployment.yaml
The controller exposes Prometheus metrics on port :8080 at the /metrics endpoint. Key metrics include:
pod_housekeeper_marked_total{namespace="<namespace>"}: Counter for Pods marked for deletion.pod_housekeeper_deleted_total{namespace="<namespace>"}: Counter for Pods successfully deleted.
- Go >= 1.24
- Docker
kubectlconfigured for a cluster (or use Kind/Minikube)
# Build the binary locally (matching the container name)
go build -o pod-housekeeper ./cmd/managergo test -v ./...You can run the controller locally against a cluster if your kubectl is configured.
# Set required environment variables (adjust values as needed)
export POD_HOUSEKEEPER_MARK_DURATION=1m
export POD_HOUSEKEEPER_DELETE_DURATION=2m
export POD_HOUSEKEEPER_EXCLUDED_NAMESPACES="kube-system"
# export KUBECONFIG=~/.kube/config # Optional: If not default
go run ./cmd/manager/main.goContributions are welcome! Please open an issue or submit a pull request.
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.