Every Tag Doesn’t Need a Sub-Second Publish Rate: Tuning OPC UA PubSub/MQTT So Your Broker Survives

Engineer monitoring shop-floor data dashboards fed by an MQTT broker

Every OPC UA PubSub over MQTT rollout survives its pilot. The pilot has ten tags, a beefy broker with nothing else on it, and a proof-of-concept dashboard that nobody depends on. Then someone scales it to a real line — a few thousand tags across a dozen PLCs, all publishing “just to be safe” at a few hundred milliseconds — and the broker starts shedding connections, the historian queue backs up, and someone on the team quietly wonders if MQTT was the wrong call. It wasn’t. The publish configuration was.

The single biggest mistake teams make migrating from Client-Server OPC UA to PubSub/MQTT is copying the publishing interval and sampling rate straight out of a vendor’s Sparkplug B example project and applying it uniformly to every tag in the Unified Namespace. Client-Server OPC UA hides this problem because the client polls on demand and the server does the work of filtering. PubSub flips the model: your edge node decides what goes on the wire, and if you don’t tell it to be selective, it will publish everything, all the time, whether anyone is subscribed or not.

Start by classifying your tags, not your PLCs

Before you touch a single publish interval, sort your tag list into classes based on how the data will actually be used downstream — not on which machine it comes from. A pressure transducer and a batch counter living on the same PLC have nothing in common from a publishing standpoint.

  • Control-loop-adjacent process variables — temperatures, pressures, speeds feeding real-time dashboards or SPC charts where operators or algorithms act within seconds. These are your sub-second candidates.
  • State and status tags — machine state, job/recipe active, fault codes. These change rarely but matter a lot when they do. Report-by-exception is the natural fit.
  • Cumulative/counter tags — production counts, cycle counters, energy meters. These need reliable delivery more than they need speed; a missed value that gets caught up on the next publish is fine, a value that vanishes silently is not.
  • Slow-moving context data — ambient conditions, quality lab results, shift/operator metadata. Minutes-level cadence is usually more than adequate.

Write this classification down as a table before you configure anything. It becomes your reference for every parameter below, and it’s the artifact you’ll want when someone asks six months from now why a given tag publishes the way it does.

Publishing interval vs. sampling interval — these are not the same knob

In OPC UA PubSub, the sampling interval is how often the server checks the underlying data source for a change, and the publishing interval is how often it’s willing to send an update out over MQTT. Teams routinely set both to the same aggressive value, which is wasteful in two different ways — you’re polling the device harder than you need to, and you’re allowing the network stack to push a message every cycle regardless of whether the value moved.

A workable pattern: set the sampling interval close to the physical reality of the data (a PID loop variable might genuinely change every 100–250 ms), but set the publishing interval a notch looser, and let deadband do the real filtering. For state and counter tags, there’s no reason to sample faster than the process can actually change state — polling a fault code every 50 ms because “the PLC scan cycle can” is a broker cost with no corresponding benefit.

Deadband is where most of the savings actually come from

Deadband — publishing only when a value moves more than a defined absolute or percentage threshold — is the single most underused lever in PubSub tuning, and it’s usually the difference between a broker that hums along and one that’s drowning in noise. A tank level sensor with normal sensor jitter of a fraction of a percent, publishing on every scan with no deadband, will generate far more traffic than the process itself justifies. Set an absolute deadband appropriate to the sensor’s real resolution and noise floor, and you cut that traffic dramatically without losing anything an operator or historian actually cares about.

Be careful with deadband on tags used for statistical process control or event reconstruction — if you need to see every micro-fluctuation for a Cpk calculation, deadband will lie to you by omission. That’s a case where a tighter deadband, or none at all, paired with a report-by-exception status flag on the side, beats trying to force one setting to do two jobs.

Match QoS to what the message is worth, not what feels safest

MQTT’s QoS levels are a broker-load decision as much as a reliability decision. QoS 0 (fire-and-forget) is appropriate for high-frequency process variables where the next value supersedes the last one anyway — if you miss one temperature reading out of twenty per second, nothing downstream cares. QoS 1 (at-least-once) is the right default for state changes, alarms, and counters, where a lost message means a real gap in the record, and the possibility of an occasional duplicate is a small price for not losing events — just make sure your consumer logic (or Sparkplug B’s birth/death and sequence-number handling) can tolerate duplicates. QoS 2 (exactly-once) has real broker and network overhead and should be reserved for the handful of tags — safety interlocks logged for compliance, batch genealogy events — where a duplicate or a loss is genuinely unacceptable. Applying QoS 2 broadly “to be safe” is one of the fastest ways to overload a broker that’s otherwise sized correctly.

A decision checklist for sub-second vs. report-by-exception

When you’re staring at a tag and not sure which bucket it belongs in, run it through these questions:

  • Does a human or a control system need to react within seconds of this value changing? If yes, it’s a candidate for tight sampling and low deadband.
  • Does this tag change value more often than it changes meaning? A vibration sensor jittering within normal tolerance is changing value constantly but not telling you anything new — deadband and/or report-by-exception.
  • Is this tag consumed by a system with its own polling or batch cadence downstream (an ERP sync, a shift report)? If so, publishing faster than that consumer reads is pure broker overhead.
  • What’s the cost of losing one update? If the next value supersedes it with no downstream harm, QoS 0 and a looser publish interval are fine. If a gap corrupts a count or hides an event, move to QoS 1 and exception-based publishing.
  • Is this a compliance or genealogy record? If yes, treat it as a discrete event, not a stream — publish on change, QoS 1 or 2, and don’t touch deadband.

Validate against the broker, not the tag list

Once you’ve set intervals, deadband, and QoS per class, the last step is watching the broker itself under real load — message rate, retained message count if you’re using them heavily, and connection/session churn from edge nodes. Most MQTT brokers used in industrial UNS deployments expose these metrics natively; if yours doesn’t, that’s worth fixing before you scale past your pilot cell. “Done right” looks boring: a message rate that tracks the actual pace of the process, a historian that isn’t perpetually catching up, and a tag configuration table that explains, for every tag, why it’s set the way it is. If you can’t answer why a given tag publishes every 200 ms instead of every 5 seconds, you haven’t finished the job — you’ve just moved the guesswork from the client poll rate to the publisher config.


This article was written with the assistance of artificial intelligence. While we aim for accuracy, the information may be incomplete, out of date, or incorrect, and should be independently verified before you rely on it for any decision. It is provided for general information only and does not constitute professional advice.

Related posts