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.