UplinkWatch

Guides

How to get an alert when a ChirpStack device stops sending

ChirpStack has no built-in alerting. Three ways to find out when a device goes quiet: the HTTP integration, the gRPC API, and a hosted watchdog.

ChirpStack will not tell you when a device stops sending. This surprises people, and it is worth being precise about why: ChirpStack is a network server, not a monitoring platform. It exposes data through integrations and an API, and leaves alerting to whatever consumes that data. There is no rule engine, no notification tab, and no "device offline" email waiting to be switched on.

So a sensor with a flat battery, a waterlogged enclosure, or a failed rejoin produces exactly the same thing as a sensor that is working perfectly and simply has not reported yet: nothing. The gap only becomes visible when somebody looks at a graph.

What ChirpStack does give you

Two things are useful here.

Device status. ChirpStack periodically issues a DevStatusReq MAC command and stores the battery level and link margin the device reports back. Configure the interval on the device profile. This is genuinely useful for catching a battery on its way down, but it does not help with a device that has stopped talking altogether — an unanswered status request is simply never answered.

The last-seen timestamp. Every device carries a lastSeenAt. This is the field that actually matters, and everything below is a way of watching it.

Option 1: the HTTP integration (recommended)

The least effort and the most reliable. Under Applications ▸ your application ▸ Integrations ▸ HTTP, set an event endpoint URL. ChirpStack will POST a JSON body to it on every uplink, containing the device EUI, name, frame counter, and the gateways that heard it.

Anything receiving those POSTs can record when each device last spoke and alert when one stops. This is a push model, so you are not polling an API on a timer and you get the gateway list for free.

The body looks roughly like this:

{
  "deviceInfo": { "devEui": "24e124707c481234", "deviceName": "IO103M" },
  "fCnt": 412,
  "rxInfo": [ { "gatewayId": "2cf7f11e15ec0000", "rssi": -97, "snr": 8.2 } ],
  "object": { "battery": 88 }
}

Note that ChirpStack posts join, ack and status events to the same URL. Treat only uplinks as evidence of life — an ack is not a working sensor.

We publish a single-file, dependency-free Python script that does exactly this: uplink-watchdog. It is MIT licensed and about 300 lines. Run it, point the integration at it, done.

Option 2: poll the API

If you would rather pull than push, you can read lastSeenAt for every device on a timer and alert on anything stale.

One trap here: ChirpStack v4 no longer ships a REST API. The v3 REST interface was a translation layer over gRPC and it was removed in v4. If you want REST you must run the separate chirpstack-rest-api container alongside it; otherwise you are talking gRPC, via the chirpstack-api package for your language.

Polling also scales worse than the webhook: with a few hundred devices you are making a large paginated request every few minutes to learn something the network server would have told you for free.

Choosing a silence threshold

This is where most home-grown monitoring goes wrong. A single global timeout does not fit a real fleet: a people counter reporting every 15 minutes and a fill-level sensor reporting once a day need windows that differ by two orders of magnitude. Set the window tight and the daily sensors cry wolf every night; set it loose and a dead people counter goes unnoticed for a day.

The approach that works is per-device and learned: track each device's own average interval between uplinks, and treat roughly three missed intervals as a fault. Three is forgiving enough to survive one dropped uplink — normal on any RF network — and tight enough to catch a real failure the same day.

One detail that is easy to get wrong: when you compute that average, discard outage-sized gaps. If a device is offline for two days and you feed that gap into the average, its expected interval balloons and the next outage takes far longer to detect. The learned cadence should reflect healthy behaviour only.

Watch the gateways too

Every uplink carries an rxInfo array naming the gateways that heard it. Recording those gives you gateway liveness with no extra configuration: if a gateway stops appearing in anybody's rxInfo, it has stopped relaying. This matters because a dead gateway looks like every device behind it dying at once, and you want to be told the cause, not the symptom.

The problem with self-hosting your own watchdog

Worth saying plainly, because it applies to every option above except the last: if your monitoring runs on your own infrastructure, nothing is watching the monitoring. If the process dies, the host reboots, or the disk fills, the alerts stop — and no alerts looks exactly like nothing being wrong.

For a hobby network that is an acceptable risk. For a fleet you are contractually responsible for, it is the failure mode that eventually bites.

Option 3: a hosted watchdog

This is what we built UplinkWatch for, so treat this section as what it is. You paste our ingest URL into the same HTTP integration field, and the watching happens on our infrastructure rather than yours. Devices and gateways register themselves, cadence is learned per device, and alerts go to email, phone push or a webhook. Free for 5 devices.

Hosted in the EU: our servers are in Helsinki, Finland, run by Hetzner. Your uplink metadata does not leave the EU.

If you only have a handful of devices and enjoy running your own things, the script in option 1 is honestly fine and you should use it.

Stop finding out from your client

UplinkWatch alerts you the moment a device or gateway goes quiet. Paste one URL into your network server. Free for 5 devices.

Start free What it does

Also available in: Deutsch · Français · Español · Português

Last updated 2026-09-03.