Device Shadow
An IoT device goes offline. A backend service wants to read its current temperature. The device isn’t there to respond. A device shadow (also called digital twin) solves this: a server-side representation of the device’s last-known state that can be read and written to even when the device is offline. When the device reconnects, it synchronizes with the shadow.
The Problem#
IoT devices are intermittently connected. Cellular, wifi, and industrial networks all have gaps. A backend service cannot rely on sending a request to a device and getting a response. The device shadow decouples the device’s online status from the ability to read or update its state.
Shadow Structure#
Each device has a shadow document with two sections:
Reported: the state the device last reported. Example: {"temperature": 23.4, "firmware": "v2.1.3", "mode": "cooling"}. Written by the device on connection.
Desired: the state the backend wants the device to be in. Example: {"mode": "heating"}. Written by backend services. When the device connects, it reads the desired state, computes the delta from its reported state, and applies changes.
Sync on Reconnect#
When a device reconnects after being offline, it subscribes to its shadow’s delta topic: $aws/things/device-123/shadow/update/delta. The shadow service publishes only the fields where desired and reported differ. The device doesn’t need to receive and apply the full desired state, just the delta.
This avoids a thundering herd problem: if 10,000 devices come back online simultaneously after a network outage, each receives only its own small delta rather than the full shadow document.
Conflict Resolution#
The device and backend can both update state simultaneously. A device reports {"mode": "cooling"} while the backend sets {"mode": "off"}. The shadow uses timestamps and last-write-wins per field. Since the device’s reported state is always authoritative for its physical reality, and the backend’s desired state is always authoritative for what the backend wants, they don’t conflict: they’re separate fields in the document.
Version Numbers#
Shadow documents have a version number incremented on every update. Conditional updates: update shadow if version == 42. Prevents lost updates when multiple backend services write to the same shadow concurrently. Same pattern as optimistic concurrency in databases.
At Oracle#
Oracle IoT Cloud’s device shadow was called “device properties.” Each device had a property store: the last-reported values, updated on every MQTT message. Backend rules read from the property store rather than querying the device directly. During a network maintenance window affecting 3,000 devices for 20 minutes, backend monitoring continued reading last-known values from the property store. When devices reconnected, they pushed updated values and any desired-state changes were applied within seconds.
What I’m Learning#
Device shadow is the right abstraction when devices are intermittently connected. It moves the system from a request/response model (which requires both parties online) to an eventually consistent sync model (which works regardless of connectivity). The desired/reported split is the key design: it cleanly separates device truth from backend intent.
Have you built systems that needed to maintain state for intermittently connected clients, and how did you handle sync on reconnect?