Schedule icon
Commands icon
Docker icon
Script icon
If icon
SlackIncomingWebhook icon

ConfigJanitor - Kubernetes unused ConfigMap and Secret cleaner

A Kestra blueprint that finds ConfigMaps and Secrets no pod references, deletes them safely, and posts a Slack summary, with a safe dry-run default.

Categories
Infrastructure

How it works

  1. list_resources (io.kestra.plugin.scripts.shell.Commands) dumps the namespace's ConfigMaps, Secrets, and Pods as JSON with kubectl.
  2. find_unused (io.kestra.plugin.scripts.python.Script) builds the set of every config a pod actually references: envFrom, env.valueFrom, volume mounts, projected volume sources, and imagePullSecrets. Anything not in that set is unused and lands in unused.txt.
  3. maybe_clean (io.kestra.plugin.core.flow.If) runs only when something is unused. Inside it, do_clean runs only in live mode (dry_run: false) and deletes each unused config with kubectl. send_summary always posts the report to Slack: what would be deleted in dry-run mode, what was deleted in live mode.
  4. There is no KV state in this flow, and that is deliberate. A janitor does not need memory: once a config is deleted it is gone, and dry-run mode is a report, not an alert.

What you get

  • Automatic cleanup of config sprawl on a schedule: leftover ConfigMaps and Secrets from deleted or reworked workloads.
  • A Slack report every run that found something: which configs, and the fact that no pod references them.
  • Safety by default: dry_run is true unless you explicitly turn it off, and only unreferenced configs are ever deleted.
  • Quiet runs when everything is referenced.

Who it's for

Anyone running Kubernetes clusters where ConfigMaps and Secrets pile up: old app configs, rotated secrets nobody removed, experiment leftovers. Works in any namespace.

Why orchestrate this with Kestra

A shell script on a cron server can diff kubectl output, but then you need somewhere to hold the cluster credentials, a place to see the run history when a config got deleted that should not have been, and a safe dry-run mode you can trust. Kestra gives you all three: credentials stay in secrets out of the YAML, every run keeps its logs and outputs where you can audit them, and the dry-run default means the first run can never surprise you.

Prerequisites

  • A Kubernetes cluster with ConfigMaps, Secrets, and Pods you can list and delete.
  • A kubeconfig client certificate and key with permissions to get, list, and delete ConfigMaps and Secrets in the target namespace.
  • Kestra with the Docker task runner available (used for the kubectl and Python steps).
  • A Slack Incoming Webhook URL.

Secrets

Secret What it is
CLIENT_CRT base64-encoded client certificate from your kubeconfig
CLIENT_KEY base64-encoded client key from your kubeconfig
MASTERURL base64-encoded Kubernetes API server URL, reachable from inside the Kestra container
SLACK_WEBHOOK base64-encoded Slack Incoming Webhook URL

In Kestra OSS, secrets come from environment variables prefixed with SECRET_ and must be base64-encoded. If the API URL uses 127.0.0.1 or localhost, replace it with host.docker.internal so the Kestra container can reach it.

Quick start

  1. Start Kestra with the four secrets above as SECRET_ environment variables.
  2. Import this flow and run it with your namespace and dry_run: true: the Slack report lists what would be deleted, and nothing is touched.
  3. Check the list. If it looks right, run once with dry_run: false: the unused configs are deleted and the Slack report says so.
  4. Run again: quiet, nothing unused left.

How to extend

  • Exclude configs by name pattern (like kube-*) with a filter in the Python step.
  • Also check Deployments scaled to zero by listing workload specs, not just running pods.
  • Run across all namespaces by looping the namespace input with a ForEach task.
  • Route reports to PagerDuty or Opsgenie by swapping the Slack task for another notification task.

Inputs

Input Type Default What it does
namespace STRING default the namespace whose configs to clean
dry_run BOOL true when true, only reports what would be deleted without deleting anything
slack_webhook STRING (required) Incoming webhook URL of the Slack channel for the cleanup report

Links

Pitfalls

  • dry_run defaults to true for a reason. Flip it to false only after you have seen a dry-run report and you trust the list.
  • "Unused" means not referenced by any running pod. A config used by a Deployment scaled to zero, an external operator, or a CI pipeline counts as unused here. The dry-run report is your chance to catch those.
  • Secrets deserve an extra look before live mode. Deleting a Secret is easy to do and annoying to reconstruct if something outside the cluster needed it.
  • The flow lists pods, not workload definitions. If you want scaled-to-zero coverage, extend the Python step to also read Deployments, StatefulSets, and DaemonSets.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.