Monitoring Thousands of Farm Sensors with Subject Wildcards
An agriculture use case for NATS: soil-moisture and weather sensors across many farms and fields, using subject hierarchy and wildcards to slice the data any way an agronomist needs.
- nats
- agriculture
- iot
- wildcards
- tutorial
The scenario
A farming co-op has many farms, each with many fields, each with many sensors (soil moisture, temperature, humidity). Agronomists need different slices of this data: one field, one farm, one sensor type across every farm, or literally everything.
Designing the subject hierarchy
The hierarchy should mirror the real-world structure, since subject wildcards only slice along existing dot-separated tokens:
farm.<farm-id>.field.<field-id>.sensor.<sensor-type>
Step 1 — start NATS
docker run -d --name nats-server -p 4222:4222 -p 8222:8222 nats:latest
nats context add local --server="nats://localhost:4222" --selectStep 2 — one field's readings
nats sub "farm.north-40.field.a.sensor.*"nats pub farm.north-40.field.a.sensor.moisture '{"pct": 34.2}'
nats pub farm.north-40.field.a.sensor.temperature '{"celsius": 21.5}'Only messages from field a on farm north-40 arrive — field b's sensors, published next, are
correctly excluded from this subscription:
nats pub farm.north-40.field.b.sensor.moisture '{"pct": 40.1}'Step 3 — every moisture sensor, across every farm and field
Using * for the farm and field tokens, and pinning the sensor type, answers a completely different
question — "what's soil moisture doing everywhere" — with the same data, no schema change:
nats sub "farm.*.field.*.sensor.moisture"nats pub farm.north-40.field.a.sensor.moisture '{"pct": 34.2}'
nats pub farm.riverside.field.a.sensor.moisture '{"pct": 51.0}'
nats pub farm.riverside.field.b.sensor.moisture '{"pct": 12.9}'All three arrive — *.field.* matches any farm and any field, as long as the trailing token is
exactly sensor.moisture.
Step 4 — literally everything, for a co-op-wide dashboard
nats sub "farm.>"> matches one-or-more trailing tokens, so it happily covers farm.north-40.field.a.sensor.moisture
and any future, deeper subject like farm.north-40.field.a.sensor.moisture.raw without a config
change.
Step 5 — persisting a season of readings with JetStream
Core pub/sub only shows readings to whoever's listening right now. For trend analysis across a growing season, capture everything into a stream:
docker rm -f nats-server
docker run -d --name nats-server -p 4222:4222 -p 8222:8222 -v nats-js-store:/data nats:latest -js -sd /datanats stream add FARM_READINGS \
--subjects "farm.>" \
--storage file --retention limits \
--max-age=180d --max-msgs=-1 --max-bytes=-1 --defaultsnats pub farm.north-40.field.a.sensor.moisture '{"pct": 34.2}'
nats stream view FARM_READINGS --subject "farm.north-40.field.a.sensor.moisture"Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
farm.*.field.*.sensor.moisture misses a farm named farm.north-40.extension | * matches exactly one token; farm ids must not themselves contain dots | Keep ids dot-free (use - or _), or restructure with an explicit separator segment |
Subscribing to farm.> returns nothing after JetStream was added | Stream capture and live subscriptions are independent — a stream doesn't forward to plain nats sub automatically | Use nats sub for live traffic and nats stream view/a JetStream consumer for historical replay, not one or the other |
nats stream add fails with nats: error: JetStream not enabled | Server was restarted without -js -sd /data | Recreate the container with JetStream flags as in Step 5 |
| Too many individual sensor subjects feels unmanageable to reason about | Expected — this is exactly what wildcards are for | Prefer 2-3 subscriptions using wildcards over one subscription per sensor |
Recap
| Subject pattern | Answers |
|---|---|
farm.north-40.field.a.sensor.* | Every sensor on one specific field |
farm.*.field.*.sensor.moisture | One sensor type, across every farm and field |
farm.> | Absolutely everything, present and future subject depth |
FARM_READINGS stream on farm.> | Historical replay/trend analysis, not just live monitoring |