Subscribe icon
If icon
DiscordIncomingWebhook icon
Fail icon
Log icon
Schedule icon

Detect Silent Device Fleets with Scheduled MQTT Heartbeat Checks

Detect silent device fleets with Kestra. Listen for MQTT heartbeats on a schedule, alert Discord and fail the run when no messages arrive.

Categories
Infrastructure

Alarms tell you when devices complain; nothing tells you when they stop talking. This blueprint inverts the usual alerting direction: on a schedule, io.kestra.plugin.mqtt.Subscribe listens on heartbeat/+ for a short bounded window, and the flow treats an empty result as the incident. Zero heartbeats means a Discord alert and a deliberately failed execution, so fleet silence is red in the executions view instead of an unremarkable green run that collected nothing.

How it works

  1. listen_heartbeats (io.kestra.plugin.mqtt.Subscribe) connects to the broker from the MQTT_SERVER secret and subscribes to heartbeat/+, where the single-level wildcard matches one heartbeat subtopic per device.
  2. The window is doubly bounded: maxDuration: PT15S caps the wait on a silent topic and maxRecords: 100 ends the task early on a healthy, chatty fleet. Either way the task completes and exposes messagesCount.
  3. check_fleet (io.kestra.plugin.core.flow.If) branches on {{ outputs.listen_heartbeats.messagesCount == 0 }}.
  4. On silence, alert_silent_fleet posts to Discord naming the window, then fail_execution (io.kestra.plugin.core.execution.Fail) marks the execution failed so dashboards, SLAs, and failure-based alerting all see it.
  5. On a healthy window, log_heartbeat_count records the count and the execution ends green.
  6. The errors block posts a Discord alert on any failed execution, covering both detected silence and a monitor that could not reach the broker; the two alerts are worded so the channel can tell them apart.
  7. A disabled-by-default Schedule trigger runs the check every 10 minutes.

What you get

  • Silence detection as a first-class signal, the inverse of alarm-driven monitoring.
  • A hard failure on empty windows, so absence of data is never mistaken for success.
  • Bounded listening windows: a silent fleet costs 15 seconds per check, never a hung execution.
  • A green/red execution history that doubles as a fleet uptime record.

Who it's for

  • IoT operators whose devices publish periodic heartbeats and who need to know when the stream stops.
  • Platform teams monitoring the broker path end to end, since silence also catches broker and network failures.
  • Anyone burned by a dashboard that stayed green while ingestion quietly died.

Why orchestrate this with Kestra

Detecting absence needs something that runs on a clock, remembers what it saw, and escalates. Kestra provides the schedule, the bounded Subscribe window, the branch that turns zero into failure, and the alert, all in one declarative file. The failed execution integrates with everything else that watches Kestra, so fleet silence propagates to the same place as any other pipeline failure.

Prerequisites

  • Devices publishing heartbeats under heartbeat/<device_id> at an interval shorter than the check cadence, so a healthy window always contains at least one message. Public test brokers accept anonymous connections; for authenticated brokers, add the username and password properties to the task from secrets.
  • A Discord incoming webhook for alerts.

Secrets

  • MQTT_SERVER: broker URI, e.g. tcp://broker.example.com:1883.
  • DISCORD_WEBHOOK_URL: Discord incoming webhook URL.

Quick start

  1. Add the MQTT_SERVER and DISCORD_WEBHOOK_URL secrets to your Kestra namespace.
  2. Execute the flow with nothing publishing and confirm the Discord silence alert plus a failed execution.
  3. Publish a heartbeat during a run, e.g. mosquitto_pub -t heartbeat/dev-001 -m '{"status": "alive"}', and confirm the execution ends green with a logged count.
  4. Set disabled: false on the every_10_minutes trigger and align maxDuration with your fleet's heartbeat interval.

What to watch out for

  • The window must exceed the fleet's heartbeat period, otherwise healthy fleets produce false silence alerts; 15 seconds suits fleets that beat every few seconds.
  • A failed silence check fires both the branch alert and the errors-block alert by design; the wording distinguishes silence from connectivity problems.

How to extend

  • Raise the bar from "any message" to a per-device roll call by parsing the stored batch at {{ outputs.listen_heartbeats.uri }} and diffing against a device registry.
  • Escalate differently on consecutive failures by pairing the flow with a Kestra flow trigger on the FAILED state.
  • Run one monitor per site by templating the topic prefix through a flow input.
  • Pair with the realtime alarm reactor blueprint so both directions, noise and silence, are covered.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.