New to Kestra?
Use blueprints to kickstart your first workflows.
Detect Kafka Connect connectors stuck PAUSED or STOPPED, resume them with ConnectorResume behind an approval gate, then re-read and fail if one is not RUNNING.
A paused Kafka Connect connector is not a failed one, which is exactly what makes it dangerous. Every self-healing monitor people write looks for FAILED connectors or FAILED tasks and restarts them. A PAUSED connector sails straight past all of them while moving zero records. The sink stops writing. The source stops reading. Nothing errors, so nothing alerts, so the pipeline looks healthy right up to the point somebody asks why yesterday is missing from the warehouse.
They get left behind for ordinary reasons. A release runbook pauses the sinks before a schema change and the step that resumes them is the step that gets skipped when the release runs late. A STOPPED connector from a planned cutover never gets started again. An operator pauses one connector to debug it on a Friday. A worker rolling restart leaves a connector reading RUNNING while its tasks are still PAUSED, which no connector-level check catches at all.
This blueprint finds them and puts them back, with the brake that the problem demands. Some connectors are paused on purpose, so resuming one of those restarts data movement a human deliberately stopped. That is why the allowlist is the safety model here rather than a convenience: a name on expected_paused is reported on every run but can never reach the resume step. On top of that, a connector has to have been silent for a whole grace window, measured from the first run that saw it, so a connector paused thirty seconds ago by a maintenance job that is still running is left alone. Resuming still needs auto_remediate true, dry_run false and a human resuming a paused execution. Afterwards it re-reads the Connect REST API and fails the run if a target is not RUNNING with every task RUNNING.
This blueprint was created by zkasuran.
auto_remediate true, dry_run false and a human resuming the Pause with approved=true. An unanswered resume reads as not approved.expected_paused is never a target. It is split out before the age check, so no combination of inputs can route it into the resume loop.min_paused_minutes window before it is eligible. The window is measured from the first run that saw that exact state, so a connector that moved from PAUSED to STOPPED starts again.freeze captures one timestamp for the run, so the detect, the cursor and the age check all agree on now.list_connectors (io.kestra.plugin.kafka.ConnectorList with expandStatus: true, read-only) returns every connector on the worker with its connector state and the state of each of its tasks.analyze marks a connector silent when its own state is PAUSED or STOPPED, as well as when it reads something else while one of its tasks is PAUSED. It records a reason per connector and splits the allowlisted names out immediately.route (io.kestra.plugin.core.flow.If) branches. With nothing silent off the allowlist it clears the cursor and logs the inventory it checked.cursor_merge keeps the first time each candidate was seen in its current state, carrying the old date forward only when the state signature matches. save_cursor (io.kestra.plugin.core.kv.Set) stores it with no ttl.split divides candidates into targets past min_paused_minutes and held ones that are still too recent.log_findings writes the whole picture to the execution log. alert posts the same thing to Slack, because a connector that is silent without being failed is worth knowing about whether or not this run is allowed to fix it.remediate_gate runs only when auto_remediate is true, dry_run is false and there is at least one target. Otherwise report_only logs what would have been resumed.approve (io.kestra.plugin.core.flow.Pause) holds the execution for a human. do_resume reads onResume.approved ?? false, so an unanswered resume does not count as approval.remediate_resume (io.kestra.plugin.core.flow.Loop, concurrencyLimit: 1) calls io.kestra.plugin.kafka.ConnectorResume once per target.settle waits verify_delay, then reverify re-reads the Connect REST API and verify_eval checks every target for RUNNING with every task RUNNING.verify_result fails the run through io.kestra.plugin.core.execution.Fail when any target is still wrong, listing exactly which. On a clean read back it prunes those names from the cursor and logs the result.errors: alert on failure.Platform teams running Kafka Connect in production. If your release runbook has a step that pauses connectors before a schema change, somebody has already shipped a day where the resume step got skipped. Data engineers who have debugged a silent pipeline where every health check was green will recognise the state this looks for.
The detect, decide, fix, re-verify loop needs state that outlives one run, a human in the middle of it and a hard stop when the fix does not work. Kestra KV holds the first-seen dates so the age window survives restarts. Pause puts a person in front of the one irreversible-feeling action without anybody watching a terminal. If keeps the write behind two inputs. Fail turns a failed re-verify into a failed execution with an alert rather than a log line nobody reads. A shell script doing the same thing would need its own state store, its own approval channel and its own alerting.
Run on Kestra 2.0.5 against Kafka 3.9.1 in KRaft mode with a Kafka Connect 7.9.0 worker, using the bundled FileStreamSource connector so the rig needs no external system.
PUT /connectors/{name}/pause: detected with reason connector PAUSED, with the running connector beside it untouched.dry_run=true: the target was listed with the resume it would have made. A GET /connectors/{name}/status straight after still read PAUSED.auto_remediate=true, dry_run=false, approved: ConnectorResume ran, the re-read returned RUNNING with every task RUNNING. The source file offset started advancing again after sitting frozen through the pause.PAUSED afterwards.expected_paused: reported as expected paused, never a target, still PAUSED at the end of the run.errors: block posted to Slack.A Kafka Connect worker whose REST API this Kestra instance can reach, plus a Slack incoming webhook.
KAFKA_CONNECT_URL: base URL of the Connect worker REST API, for example http://connect:8083.SLACK_WEBHOOK_URL: Slack incoming webhook for the findings alert plus the failure alert.If your Connect worker sits behind basic auth, add username and password to each Kafka task from secrets of your own.
connect_url: Connect REST endpoint. Defaults to the secret above.expected_paused: connector names allowed to stay paused. Never resumed.min_paused_minutes: minutes a connector must stay silent before it is eligible. Defaults to 30.verify_delay: wait before the re-read. Defaults to PT10S.auto_remediate: defaults to false.dry_run: defaults to true.expected_paused and run it again. Those names move to the expected-paused line of the report.auto_remediate=true and dry_run=false, read the pause description, then resume the execution with approved=true.hourly_scan trigger once you are happy with the report. It runs in safe mode, so it reports without changing anything until you give it different inputs.min_paused_minutes to minutes for a cluster where no legitimate pause lasts long. Raise it to days where migrations run slow.expected_paused at a task that reads it.io.kestra.plugin.kafka.ConnectorGetOffsets before the resume to record where each connector stood, which makes it easy to show how far behind it fell.Switch on the target names.