Node-RED

Node-RED Context Storage: Flow, Global and Persistent

Node-RED context lets flows remember things between messages. This explainer covers the three scopes, memory versus file storage, multiple stores, async access and a practical pattern for keeping the latest MQTT value per device.

Diagram of Node-RED context scopes with node, flow and global storage holding the latest MQTT values
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

Node-RED context is the built-in key-value storage that lets a flow remember values between messages, such as the last temperature a sensor reported or a counter of alarms today. This explainer covers the three context scopes, how to make context survive restarts, how to use several stores at once and a clean pattern for keeping the latest MQTT value for every device.

Why Flows Need State

Messages in Node-RED are independent. When an mqtt in node emits a reading, the next node knows only what is inside that message. Many real tasks need more than that:

  • Comparing a value to the previous one to detect a change or a trend.
  • Answering an HTTP request with the most recent reading, even though no MQTT message is arriving right now.
  • Counting events per shift, or remembering when an alarm started.
  • Combining values from two topics that arrive at different times.

MQTT itself offers one form of memory through retained messages, where the broker stores the last message on a topic and hands it to new subscribers. That is great for state shared between clients, and you should use it. Context is the complementary tool for state that lives inside Node-RED. If you are unsure which side a responsibility belongs on, our explainer on what an MQTT broker does draws the line.

The Three Node-RED Context Scopes

Scope Visible to Typical use Access in a function
Node Only the node that set it Deadband filters, counters inside one function context.get() / context.set()
Flow All nodes on the same tab (or inside the same subflow instance) Latest values used by several nodes on one tab flow.get() / flow.set()
Global Every node in the Node-RED instance Shared configuration, lookup tables global.get() / global.set()

Pick the narrowest scope that works. A value in global context can be changed by any node on any tab, which makes bugs hard to trace. Node context is private, so nothing else can break it.

You do not have to write code to use context. The Change node can set, read, move and delete flow. and global. properties, and the Inject, Switch and Template nodes can read them. The Context Data tab in the editor sidebar shows current values for each scope and lets you refresh or delete entries while debugging.

Subflows have one extra twist: flow context inside a subflow belongs to that instance. A subflow can reach its parent flow's context with the $parent. prefix, such as flow.get("$parent.lastTemp").

Memory Versus Persistent Context

By default all context is stored in memory. It is fast, but it disappears when Node-RED restarts, when the container is recreated or when the host reboots. For anything you cannot easily rebuild, switch on file-backed storage.

Open settings.js (in ~/.node-red/ on a standard install, or /data/ in the Docker image) and find the contextStorage section:

// settings.js
module.exports = {
    // ...
    contextStorage: {
        default: { module: "memory" },
        file:    { module: "localfilesystem" }
    },
    // ...
};

Restart Node-RED. You now have two stores: default, which stays in memory, and file, which writes JSON files under the context/ folder of your user directory. Nodes choose a store explicitly:

flow.set("lastReading", msg.payload, "file");   // persisted
flow.set("scratch", 42);                        // memory (default store)
const last = flow.get("lastReading", "file");

In the Change node and other editors, a small store selector appears next to flow. and global. fields once you have more than one store.

If you prefer everything persistent, set default: { module: "localfilesystem" } instead. Keep in mind how the file store works: values are cached in memory and written to disk on an interval, every 30 seconds by default, controlled by the flushInterval option. A hard crash can lose the last few seconds of changes. That is fine for latest values and counters, but context is not a database. For historical data, write to a time-series store such as InfluxDB, as shown in our Node-RED InfluxDB guide.

Also note that only JSON-serialisable values persist reliably. Functions, timers and class instances will not come back intact after a restart, so store plain objects, numbers and strings.

Sync and Async Access

With the memory and localfilesystem stores, get and set work synchronously as shown above. Some third-party stores, such as Redis-based modules, are asynchronous and require callbacks:

flow.get("count", "redis", (err, count) => {
    if (err) { node.error(err, msg); return; }
    count = (count || 0) + 1;
    flow.set("count", count, "redis", (err) => {
        if (err) { node.error(err, msg); return; }
        msg.payload = count;
        node.send(msg);
        node.done();
    });
});

Writing code with callbacks from the start costs little and means you can change the store later without rewriting every function. Be aware that a get followed by a set is not atomic. With very high message rates on an async store, two messages can read the same old count. Keep counters in node context where only one node writes them, or batch updates.

You can also read and write several keys at once by passing arrays: flow.get(["a", "b"]) returns an array of values.

Pattern: Keep the Latest MQTT Value per Device

A common Node-RED context job is a live "last known value" table: one entry per device, updated on every message, readable from anywhere in the flow. Publish some test data to the ConnectMQ public MQTT test broker, replacing c8d1 with your own random string:

mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-ctx-c8d1 \
  -t "connectmq/demo/c8d1/boiler/tank1/telemetry" -m '{"level": 71.5, "temp": 63.2}'
mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-ctx-c8d1 \
  -t "connectmq/demo/c8d1/boiler/pump2/telemetry" -m '{"rpm": 1450, "temp": 41.0}'

Subscribe in Node-RED with an mqtt in node on connectmq/demo/c8d1/+/+/telemetry, output set to parsed JSON, and wire it to this function:

// Function "store latest"
const parts = msg.topic.split("/");
const deviceId = parts[3] + "/" + parts[4];       // e.g. "boiler/tank1"

const latest = flow.get("latest", "file") || {};
latest[deviceId] = {
    values: msg.payload,
    ts: Date.now()
};
flow.set("latest", latest, "file");

node.status({ text: Object.keys(latest).length + " devices" });
return null;

Now any node on the tab can read the table. An http in node on GET /latest followed by this function and an http response node gives you a tiny API:

// Function "serve latest"
const latest = flow.get("latest", "file") || {};
const staleMs = 5 * 60 * 1000;
const now = Date.now();

msg.payload = Object.entries(latest).map(([id, v]) => ({
    device: id,
    values: v.values,
    ageSeconds: Math.round((now - v.ts) / 1000),
    stale: now - v.ts > staleMs
}));
return msg;

The stale flag is the important detail. A latest-value table that never expires happily shows a reading from a device that died yesterday. Always store a timestamp with the value.

Node-RED Context Best Practices Checklist

  1. Use the narrowest scope: node, then flow, then global.
  2. Enable a file store and choose it explicitly for values you cannot rebuild.
  3. Store timestamps alongside values so you can detect stale data.
  4. Avoid huge objects; large context entries slow every flush and every sidebar refresh.
  5. Prefix global keys by purpose, for example cfg_ for configuration and cache_ for lookup data.
  6. Do not store secrets in context. Credentials belong in configuration nodes and environment variables.
  7. Use retained MQTT messages for state other clients need, and context for state only Node-RED needs.
  8. Back up the context/ folder along with your flows. Running Node-RED in containers? Our Node-RED Docker guide shows how to keep /data on a persistent volume.

The official context documentation covers the store API and configuration options in full. For the code side of these patterns, see our Node-RED function node guide, and for the bigger picture of connecting flows to brokers, the Node-RED MQTT tutorial.

Pair Node-RED Context with ConnectMQ

Context keeps Node-RED's own memory; the broker keeps everyone else's. ConnectMQ is a fully managed MQTT service with retained messages, MQTT 5 session expiry and TLS, so devices, dashboards and Node-RED all see consistent last-known values. Its portal adds a live topic explorer and threshold alert rules for quick checks without building a flow. Start on the free Serverless plan at app.connectmq.com, no credit card required, and connect Node-RED with the generated username, password and client ID.

Frequently Asked Questions

What is the difference between flow and global context in Node-RED?

Flow context is shared only by nodes on the same tab, while global context is shared by every node in the Node-RED instance. Use flow context unless several tabs genuinely need the same value.

How do I make Node-RED context survive a restart?

Add a localfilesystem store under contextStorage in settings.js, restart Node-RED and write to that store, for example flow.set("key", value, "file").

Where does Node-RED store persistent context files?

In a context folder inside the Node-RED user directory, usually ~/.node-red/context or /data/context in Docker, organised by scope.

Should I use context or retained MQTT messages for latest values?

Use retained messages when other MQTT clients need the latest value, and context when only Node-RED needs it, for example to serve an API or compare readings. Many flows use both.

Related articles

Node-RED OPC UA Client to MQTT with ISA-95 Topics

Use node-red-contrib-opcua to subscribe to OPC UA variables on a PLC or simulation server, map node IDs to ISA-95 style MQTT topics, and publish values with quality and timestamps.

9 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.