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

JobWatch - Kubernetes failed Job watcher

A Kestra blueprint that watches Kubernetes Jobs, detects failures, and alerts on Slack without spamming.

Categories
Infrastructure

How it works

  1. list_jobs (io.kestra.plugin.scripts.shell.Commands) runs kubectl get jobs in the target namespace and saves the full job list as JSON. It authenticates with a client certificate and key passed as Kestra secrets, the same pattern the Kubernetes gallery blueprints use.
  2. find_trouble (io.kestra.plugin.scripts.python.Script) reads the remembered state from the KV store, then checks every job's status. Trouble is any failed pods, or a Failed condition being True. Anything already alerted on is dropped.
  3. maybe_alert (io.kestra.plugin.core.flow.If) runs only when there is something new to report, and send_alert posts the failed jobs to Slack with the reasons and a kubectl command to inspect each one.
  4. save_state (io.kestra.plugin.core.kv.Set) writes back the new list of troubled job names as one JSON value. Deleted jobs drop out on their own, so a future failure alerts again.

What you get

  • A daily job health check for any namespace, no agent to install inside the cluster.
  • Slack alerts only for new trouble: which job, why it failed, and the kubectl command to look at it.
  • No alert spam: the KV-backed memory remembers who was already reported, and forgets jobs that were deleted.
  • Quiet runs when every job succeeded.

Who it's for

Anyone running batch work on Kubernetes: migrations, backups, report generators, data loads. A failed job that nobody notices is a silent outage. Works on any cluster you can reach with a client certificate: minikube, kind, managed clusters.

Why orchestrate this with Kestra

A shell script on a cron server can list jobs, but then you need somewhere to keep the "already alerted" state, somewhere to hold the secrets, and a place to see the run history when an alert looks wrong. Kestra gives you all three: the KV store is the memory, secrets stay out of the YAML, and every run keeps its logs and outputs where you can inspect them.

Prerequisites

  • A Kubernetes cluster reachable from where Kestra runs, with a client certificate and key for authentication.
  • 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 for the cluster
CLIENT_KEY base64-encoded client key for the cluster
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.

Quick start

  1. Start Kestra with the four secrets above as SECRET_ environment variables.
  2. Import this flow and run it with the namespace you want to watch.
  3. Create a test job that always fails (kubectl create job jw-fail --image=busybox -- sh -c 'exit 1') and run again: the Slack alert lists the job and the failure.
  4. Run once more without changing anything: no second alert. Delete the job and run again: quiet, and the state is cleared.

How to extend

  • Add a failed-pod threshold so a job with one retry failure stays quiet.
  • Lower the schedule to every hour for faster detection by changing the cron trigger.
  • Deploy one copy of the flow per namespace you want to watch, changing only the namespace input.
  • Route alerts 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 jobs to watch
slack_webhook STRING (required) Incoming webhook URL of the Slack channel for the alerts

Links

Pitfalls

  • Jobs are not cleaned up automatically by default, so a failed job stays visible until you delete it. The dedup means you hear about it once, then it stays quiet until the job is gone.
  • A job whose pods are still retrying (backoff) may not have failed set yet. The Failed condition with reason BackoffLimitExceeded catches the terminal state.
  • Make sure the MASTERURL is reachable from inside the Kestra container. localhost inside the container is not your host: use host.docker.internal when Kestra runs in Docker on the same machine as the cluster.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.