Back to Spy NATS
Spy NATS Blog/ Monitoring Thousands of Farm Sensors with Subject Wildcards

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.

4 min read
  • 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

terminal 1
docker run -d --name nats-server -p 4222:4222 -p 8222:8222 nats:latest
nats context add local --server="nats://localhost:4222" --select

Step 2 — one field's readings

terminal 2 — an agronomist watching one specific field
nats sub "farm.north-40.field.a.sensor.*"
terminal 3 — sensors on that field report in
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:

terminal 4 — a different field, should NOT show up above
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:

terminal 2 — every moisture sensor, any farm, any field
nats sub "farm.*.field.*.sensor.moisture"
terminal 3 — publish from multiple farms/fields
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

terminal 2 — every reading, from every farm, every field, every sensor type
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:

terminal 1 — restart with JetStream
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 /data
terminal 2 — one stream captures the whole co-op
nats stream add FARM_READINGS \
  --subjects "farm.>" \
  --storage file --retention limits \
  --max-age=180d --max-msgs=-1 --max-bytes=-1 --defaults
terminal 3 — publish a season's worth (simulated) and replay any slice later
nats 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

SymptomLikely causeFix
farm.*.field.*.sensor.moisture misses a farm named farm.north-40.extension* matches exactly one token; farm ids must not themselves contain dotsKeep ids dot-free (use - or _), or restructure with an explicit separator segment
Subscribing to farm.> returns nothing after JetStream was addedStream capture and live subscriptions are independent — a stream doesn't forward to plain nats sub automaticallyUse 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 enabledServer was restarted without -js -sd /dataRecreate the container with JetStream flags as in Step 5
Too many individual sensor subjects feels unmanageable to reason aboutExpected — this is exactly what wildcards are forPrefer 2-3 subscriptions using wildcards over one subscription per sensor

Recap

Subject patternAnswers
farm.north-40.field.a.sensor.*Every sensor on one specific field
farm.*.field.*.sensor.moistureOne 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

SpyNATS is an independent tool and is not affiliated with, sponsored by, or endorsed by Synadia Communications or the NATS.io project.