DDS Data Distribution Service in 2026: RTPS, QoS, Security and ROS 2 Guide
Most messaging systems answer one question: how do I get bytes from A to B? The DDS data distribution service answers a harder one: how do I let dozens of independently built components share typed, time-sensitive state, with no central broker, while each side states exactly how fresh, how reliable and how durable that data must be? That difference explains why DDS runs inside combat management systems, air traffic control, autonomous vehicles and, through ROS 2, a large share of the world’s research and production robots.
It also explains why DDS is routinely misconfigured. Teams treat it as “MQTT without a broker”, leave every default in place, and then discover that discovery traffic floods a Wi-Fi network, that a reader silently never matches a writer, or that a reliable stream stalls behind one slow subscriber. Almost every one of those failures traces back to a misunderstanding of the data-centric model underneath.
This guide rebuilds the picture from the standards up and flags what has changed by October 2026. You will leave with a working mental model of DCPS and RTPS, the QoS matching rules that decide who talks to whom, a discovery and tuning playbook, and a clear view of when DDS is the wrong tool.
What this covers: the OMG specification family and current versions, the DCPS data-centric model, the RTPS wire protocol and its reliability handshake, QoS request-versus-offered semantics, SPDP and SEDP discovery, DDS Security, XTypes, DDS-RPC, the vendor landscape, ROS 2 and Zenoh, real-time and TSN tuning, a comparison with MQTT and OPC UA PubSub, failure modes, and runnable configuration and code.
Context and Background
DDS was adopted by the Object Management Group (OMG) in 2004 and has been refined for two decades. The API specification, simply called DDS, has its most recent formal version at 1.4 (March 2015) according to the OMG DDS specification page. That number looks stale, but it is deliberate. The API changed little because the real evolution happened in companion specifications that wrap the core.
The wire protocol is DDSI-RTPS (the DDS Interoperability Real-Time Publish-Subscribe protocol). Its latest formal version is 2.5, published April 2022, preceded by 2.3 (2019), 2.2 (2014), 2.1 (2010) and 2.0 (2008), per the OMG DDSI-RTPS page. RTPS is what lets a Cyclone DDS participant exchange data with a Fast DDS or RTI Connext participant without any shared code. DDS Security is at version 1.2, dated February 2026, after 1.1 (2018) and 1.0 (2016), per the OMG DDS Security page. DDS-XTypes, the extensible type system, has 1.3 (February 2020) as its last formal release, and a 1.4 beta was listed as in process in August 2026 on the OMG XTypes page. Treat the beta as unfinished until OMG promotes it.
Where does DDS sit among the industrial protocols? It is a data-centric peer-to-peer bus for control-plane and sensor-plane data inside a trust domain, typically on one LAN or a handful of routed segments. It is not an enterprise telemetry transport to the cloud (that is MQTT’s home) and not an information-modelling framework for plant equipment (that is OPC UA). Our DDS vs MQTT vs OPC UA comparison covers that boundary, and the broader IoT protocol selection guide places it among the other options.
Two market facts shape the 2026 landscape. First, ROS 2 made DDS the default middleware of robotics, but it did so through an abstraction layer, the ROS Middleware Interface (RMW), which now also hosts a non-DDS alternative based on Zenoh. Second, the Eclipse Cyclone DDS and eProsima Fast DDS projects are open source, so the “DDS is expensive” reputation applies only to certified commercial stacks such as RTI Connext, which sells safety-certifiable builds and tooling. The rest of this guide is vendor-neutral unless noted.
A note on terminology. “Data-centric” means the middleware understands the structure and lifecycle of the data, not only its transport. A topic has a type with named fields and optional keys. Each key value is an instance with its own history, so a fleet of 500 vehicles publishing on one VehicleState topic is 500 independent data streams that DDS can order, expire and cache separately. That feature, often missed by newcomers, is the main reason DDS is not interchangeable with a topic-string broker.
The Core Model: DCPS, the Global Data Space and RTPS
In short: DDS exposes a Data-Centric Publish-Subscribe (DCPS) API in which applications read and write typed samples to a shared “global data space”. The Real-Time Publish-Subscribe (RTPS) protocol moves those samples over UDP, and Quality of Service (QoS) contracts between each writer and reader decide delivery behaviour. There is no broker; discovery is peer-to-peer.

Figure 1: Layered view of the DDS data distribution service. Applications use DCPS entities, QoS contracts gate matching, and RTPS carries samples over UDP, shared memory or TCP.
Figure 1 shows the layers. Applications talk to DCPS entities. QoS policies sit between writers and readers as a compatibility contract. RTPS beneath handles discovery, reliability and fragmentation, and below RTPS the transport may be UDP unicast or multicast, shared memory, or TCP and TLS for awkward networks.
The DCPS entity hierarchy
A DomainParticipant is the root entity, the membership card for one DDS domain. A domain is identified by an integer domain ID, and participants in different domains never see each other. Under the default RTPS port mapping, valid IDs run from 0 to 232 because port numbers are derived from the ID and must stay below 65,536.
A participant owns Publishers and Subscribers, which are grouping and QoS-defaulting containers, plus Topics. A Topic binds a name to a registered type. On top of these sit the two entities that matter in practice: the DataWriter, which publishes samples of one topic, and the DataReader, which receives them. A reader may also be filtered with a content filter or a time-based filter, so that a subscriber to VehicleState only receives samples where speed > 30, and the filter is evaluated at the writer side by many implementations to save bandwidth.
Keys deserve their own sentence. Declaring a field as a key in IDL, for example @key string vehicle_id, makes each distinct value an instance. History depth, deadline, lifespan, ownership and dispose semantics are all applied per instance. Without keys, the entire topic is a single stream.
Why “global data space” is more than marketing
Consider a classic pub-sub system: a broker holds the topic, and clients publish and subscribe to it. State lives in the broker, and late joiners get what the broker retained. In DDS the “space” is virtual. Every writer caches its own recent samples, according to its HISTORY and DURABILITY settings, and when a late-joining reader matches, the writer replays the cached samples. Durable state therefore lives with the data producers, and the failure of any one node removes only that node’s data.
This is the first of two original framings I use to reason about DDS. Treat each DataWriter as a small, distributed, in-memory database table with a replication contract. The QoS policies are the table’s replication and retention settings. Once you think this way, questions such as “will my new subscriber get the last value?” become answerable: it will if the writer has TRANSIENT_LOCAL durability, a non-zero history depth, and is still alive.
The RTPS wire protocol in one page
RTPS messages are UDP datagrams (or the equivalent on another transport). Each begins with a header carrying the ASCII magic “RTPS”, the protocol version, a two-byte vendor ID and a 12-byte GUID prefix identifying the sending participant. The body is a sequence of submessages: DATA carries a sample, DATA_FRAG carries one fragment of a large sample, HEARTBEAT announces the range of sequence numbers a writer holds, ACKNACK tells the writer what a reader has and what it lacks, and GAP states that certain sequence numbers will never arrive (for example because they were filtered or have been dropped from history). INFO_TS and INFO_DST carry timestamps and addressing hints.
Every writer assigns each sample a monotonically increasing 64-bit sequence number, and every reader tracks per-writer state. This per-matched-pair state is what makes reliability efficient and also what makes DDS scale differently from a broker: the cost of a reliable writer grows with its number of matched readers.
RTPS also defines a default port mapping. With the standard parameters PB = 7400, DG = 250 and PG = 2, the discovery multicast port for domain d is 7400 + 250d. Domain 0 therefore uses UDP 7400 for multicast discovery, and domain 1 uses 7650. Unicast ports for each participant are offset further by a participant index. The default discovery multicast address is 239.255.0.1. Firewalls, containers and cloud networks that block multicast or restrict port ranges are the number-one cause of “my nodes cannot see each other”.
QoS Policies: The Contract That Decides Who Talks
DDS defines roughly two dozen QoS policies. Most are local settings. A subset is request-versus-offered (RxO): the writer offers a level, the reader requests a level, and the match succeeds only if the offer is at least as strong as the request. If it is not, no data flows and both sides receive an “incompatible QoS” status. This silent-until-you-check behaviour is by design, and it is the most common support ticket in DDS projects.
The policies you will use daily
RELIABILITY is BEST_EFFORT or RELIABLE. A best-effort writer never retransmits. A reliable one keeps samples until readers acknowledge them, and uses HEARTBEAT and ACKNACK to repair loss. A reader requesting RELIABLE will not match a best-effort writer, but a best-effort reader will match either.
DURABILITY orders VOLATILE < TRANSIENT_LOCAL < TRANSIENT < PERSISTENT. Volatile means late joiners get nothing historical. Transient-local means the writer replays its cache while it is alive. Transient and persistent require a durability service (a separate process or built-in service in some stacks) to outlive writers, and support is vendor-dependent.
HISTORY is KEEP_LAST(n) or KEEP_ALL, applied per instance. Together with RESOURCE_LIMITS it bounds memory. KEEP_ALL plus RELIABLE can block the writer when a slow reader fills the queue, which is what you want for lossless logging and a hazard for a control loop.
DEADLINE is a period within which a new sample must arrive per instance (reader side) or be written (writer side). RxO rule: the offered period must be less than or equal to the requested one. When missed, a status callback fires. Deadline is the cheapest built-in sensor-freshness watchdog in any messaging system.
LIVELINESS declares how a writer proves it is alive: AUTOMATIC (the middleware does it), MANUAL_BY_PARTICIPANT or MANUAL_BY_TOPIC (the application must assert), with a lease duration. It detects hung applications, not just dead processes, if you use the manual modes.
LIFESPAN expires samples after a duration. LATENCY_BUDGET is a hint that lets the middleware batch. TRANSPORT_PRIORITY hints at DSCP or priority mapping. OWNERSHIP (SHARED or EXCLUSIVE with a strength) enables hot-standby publishers: among several writers of the same instance, only the highest-strength live writer is delivered. DESTINATION_ORDER chooses source or reception timestamp ordering. PRESENTATION controls coherent and ordered access across writers in a group.
Compatibility cheat sheet
| Policy | Ordering of strength | Match rule |
|---|---|---|
| Reliability | BEST_EFFORT < RELIABLE | offered >= requested |
| Durability | VOLATILE < TRANSIENT_LOCAL < TRANSIENT < PERSISTENT | offered >= requested |
| Deadline | longer period is weaker | offered period <= requested period |
| Liveliness | AUTOMATIC < MANUAL_BY_PARTICIPANT < MANUAL_BY_TOPIC, and shorter lease is stronger | offered kind >= requested, offered lease <= requested |
| Ownership | SHARED vs EXCLUSIVE | kinds must be identical |
| Destination order | BY_RECEPTION < BY_SOURCE | offered >= requested |
| Presentation | access scope and ordered/coherent flags | offered scope >= requested |
A worked deadline and history calculation
Suppose an IMU publishes at 200 Hz, so one sample every 5 ms. You want the control node to detect a stall after missing two consecutive samples. Set the reader deadline to 12 ms, leaving 2 ms of jitter margin over two periods, and have the writer offer 5 ms (which satisfies offered <= requested). A scheduling hiccup that delays one sample by 8 ms does not fire the status; a stall of 15 ms does.
For history, a KEEP_LAST(1) reader always sees the newest sample only, which is correct for state topics such as “current pose”. For a lossy radio link, a KEEP_LAST(10) writer with RELIABLE gives 50 ms of repair window at 200 Hz: a reader that loses a datagram can still request retransmission for roughly 10 samples back. Note that these numbers are illustrative arithmetic, not measurements, but the relationship (repair window = depth x period) is exact.
Why QoS mismatches are silent, and how to catch them
Because incompatible endpoints simply do not match, nothing appears on the wire and no sample is dropped, so there is no packet-loss counter to alarm on. You must monitor the REQUESTED_INCOMPATIBLE_QOS and OFFERED_INCOMPATIBLE_QOS statuses, or in ROS 2 run ros2 topic info -v, which prints each endpoint’s QoS so you can compare them by eye. Build this check into integration tests: a topic with a writer and a reader that never matched is a bug you can find in CI.
Discovery, Reliability and Security Walk-through
SPDP and SEDP: discovery without a broker
Discovery has two stages, both defined in the RTPS specification. The Simple Participant Discovery Protocol (SPDP) is a best-effort announcement: each participant periodically sends a DATA(p) message describing itself (GUID, vendor, supported builtin endpoints, locators where it listens, lease duration) to the well-known multicast address and port for the domain, and optionally to configured unicast peers. Receivers add the sender to their participant table. If no announcement arrives within the lease duration, the participant is removed along with all its endpoints.
Once two participants know each other they open the Simple Endpoint Discovery Protocol (SEDP). This is a pair of built-in reliable topics, one for publications and one for subscriptions, that carry each DataWriter and DataReader’s topic name, type name, QoS and key. Each side compares the remote endpoint’s QoS with its own and performs the match. Only after a match does user data flow.
The announcement period and lease are implementation parameters, and defaults differ by vendor and version. The original version of this article quoted 30-second heartbeats and a 300-second lease as universal defaults, which is not safe to assume; check your stack’s documentation, and set them explicitly if failure-detection time matters. Shorter leases detect failure faster and cost more discovery traffic.

Figure 2: From SPDP announcement through SEDP matching to a reliable DATA, HEARTBEAT and ACKNACK exchange.
The discovery scaling problem
With simple discovery, every participant eventually learns every other participant’s endpoints, even endpoints on topics it does not use. For N participants, the number of participant-to-participant relationships is N(N-1)/2, and each carries SEDP state. On a robot with 20 nodes this is invisible. On a 400-node simulation farm, or over Wi-Fi where multicast is sent at the lowest basic rate, it is a real load that appears as bursty startup latency and CPU spikes.
Mitigations exist at three levels. First, partition by domain ID or, for ROS 2, with ROS_AUTOMATIC_DISCOVERY_RANGE and ROS_STATIC_PEERS, so that discovery is confined to localhost or an explicit list. Second, use a discovery server: Fast DDS offers a Discovery Server mode where clients register with one or more servers and query only the information they need, and RTI offers Cloud Discovery Service for the same role. Third, bridge across WAN or Wi-Fi boundaries with a gateway such as a Zenoh bridge, covered later, so that DDS discovery stays on each local segment.
The reliability handshake, step by step
Take one reliable writer W and one reliable reader R. W writes samples with sequence numbers 1 to 5 and sends them as DATA submessages. R receives 1, 2 and 4. Packet 3 and 5 were lost. W then sends a HEARTBEAT stating “I hold sequence numbers 1 through 5”. R compares that with what it has and replies with an ACKNACK carrying a bitmap: “acknowledged through 2, missing 3 and 5”. W retransmits 3 and 5. Once R’s next ACKNACK says everything through 5 is received, W may release them from its queue, subject to history settings.
Three observations follow. Sample 4 is received but held until 3 arrives only if ordered delivery requires it; DDS readers deliver in sequence order per writer. Recovery latency is bounded below by the heartbeat period plus one round trip, so heartbeat period is your repair-latency knob. And if the writer’s history is KEEP_LAST(n) and sample 3 has been overwritten before the NACK arrives, the writer answers with a GAP, and the reader moves on without it. Reliable in DDS means “reliable within the history window”, not “lossless forever”.
Fast retransmission is tunable. Most stacks expose a heartbeat period, a “nack response delay” and a “nack suppression duration”. A very short heartbeat period gives low repair latency and a storm of control traffic if you have many readers; piggybacking a heartbeat on the final data packet of a burst is a standard optimisation.
Large samples and fragmentation
A UDP datagram on Ethernet carries about 1,472 bytes of payload at a 1,500-byte MTU. RTPS fragments larger samples into DATA_FRAG submessages sized to the transport’s maximum message size, and the reader reassembles. Fragmentation interacts badly with loss: losing one fragment of a 2 MB point cloud requires a NACK_FRAG repair of that fragment, but on a lossy link a sample of hundreds of fragments may almost never arrive intact under best-effort. For image and lidar data, two options dominate: shared-memory transport between processes on one host (Cyclone DDS integrates with Eclipse Iceoryx; Fast DDS and RTI have their own shared-memory transports), and compressed or tiled payloads sent over reliable QoS with an explicit flow controller.
Back-pressure and the slow-reader problem
A reliable writer with KEEP_ALL history and a stalled reader will eventually block in write() once its resource limits are reached, with a configurable max_blocking_time. This is correct for an audit log and harmful for a 1 kHz control loop sharing the same participant. The usual fixes are separate topics (and sometimes separate participants) for control and bulk data, KEEP_LAST for state-like data, and best-effort for streams where newer data supersedes older.
Security, Types and RPC: The Companion Specifications
DDS Security
Raw DDS on a LAN is unauthenticated: any process that joins the domain can read and write any topic. DDS Security (OMG, version 1.2 as of February 2026) adds a pluggable model with five service plugin interfaces: Authentication, Access Control, Cryptographic, Logging and Data Tagging. The specification also defines built-in implementations that every compliant product ships, so interoperability does not depend on custom plugins.
The built-in authentication plugin (DDS:Auth:PKI-DH) uses X.509 certificates issued by a certificate authority you control, with a Diffie-Hellman exchange to derive a shared secret between participants. The built-in access-control plugin (DDS:Access:Permissions) evaluates two signed XML documents: a Governance document that declares domain-wide rules (must discovery be protected? must topics be encrypted or only signed?) and a Permissions document per participant that lists which topics it may publish or subscribe to. Both documents are signed (S/MIME) by a permissions CA so they cannot be tampered with. The built-in cryptographic plugin (DDS:Crypto:AES-GCM-GMAC) provides AES-GCM encryption and GMAC authentication, with options to protect at the sample, submessage or full RTPS message level and to choose per-topic protection.
Two practical details matter. First, protecting discovery is a governance choice: if discovery is left unprotected, an observer can map topic names and endpoints even if the payload is encrypted. Second, cryptography costs CPU on every sample and breaks zero-copy shared-memory paths in some stacks, so benchmark with security on before you size hardware.
In ROS 2 this appears as SROS 2, which generates a keystore of certificates and signed governance and permissions files per node, enabled with environment variables such as ROS_SECURITY_ENABLE=true and ROS_SECURITY_STRATEGY=Enforce. Our zero-trust architecture for industrial OT article places this inside a wider segmentation strategy.
XTypes: evolving data models without breaking fleets
Real systems outlive their data definitions. DDS-XTypes defines type extensibility and type assignability. A struct may be @final (never changes), @appendable (fields may only be added at the end) or @mutable (fields identified by member IDs, reorderable and optional). A reader built against version 1 of a type can match a writer publishing version 2 if the types are assignable under the rules, and the extra fields are ignored. Participants exchange a compact TypeObject description during SEDP, so that tooling can discover a type without compiled-in knowledge, and the XCDR2 serialisation format supports these extensibility kinds.
The practical rule: mark types @appendable or @mutable from the start if you ship to a fleet and cannot upgrade all nodes at once. Switching a deployed @final type is a flag-day change. Support for XTypes features varies across vendors and RMW layers, and ROS 2 message types are historically generated as final, so confirm what your stack actually does before relying on cross-version matching.
DDS-RPC and DDS-XRCE
DDS-RPC (version 1.0, 2017) defines request-reply over two topics with correlation identifiers, giving DDS a standard service model rather than every project inventing its own. ROS 2 services and actions are built on the same request-reply-over-topics pattern, though with ROS-specific conventions. DDS-XRCE defines an eXtremely Resource Constrained Environments protocol so that microcontrollers can talk to a DDS network through an agent; Micro XRCE-DDS and micro-ROS are its best-known implementations. The agent keeps the full DDS entities on behalf of the small device.
The vendor landscape
| Stack | Licence and model | Notes |
|---|---|---|
| Eclipse Cyclone DDS | Open source (Eclipse Public License 2.0 / Eclipse Distribution License 1.0) | Compact C core; Iceoryx shared memory; widely used in ROS 2 and automotive |
| eProsima Fast DDS | Open source (Apache 2.0) | Default RMW in recent ROS 2 LTS releases; Discovery Server; rich XML profiles |
| RTI Connext | Commercial | Safety-certifiable editions, rich tooling, Routing Service, Cloud Discovery |
| OpenDDS | Open source | C++ with Java bindings, common in defence programmes |
| CoreDX, GurumDDS and others | Commercial | Embedded and certified niches |
Interoperability across them is real because of RTPS but not total. Differences in default QoS, discovery tuning and XTypes support mean you should run a cross-vendor test in your lab before mixing stacks in production.
DDS in ROS 2, Zenoh Bridging and Real-Time Tuning
How ROS 2 uses DDS
ROS 2 does not call DDS directly. Its client libraries sit on the RMW interface, and each RMW package (rmw_fastrtps_cpp, rmw_cyclonedds_cpp, rmw_connextdds) maps ROS concepts onto DDS entities: a ROS node becomes a DomainParticipant, a ROS topic becomes a DDS topic with a mangled name prefix (rt/ for topics), and publishers and subscriptions become DataWriters and DataReaders. The ROS domain ID, set with ROS_DOMAIN_ID, is the DDS domain ID. You choose a middleware at run time with RMW_IMPLEMENTATION. In ROS 2 Jazzy, Fast DDS remains the default and Cyclone DDS is the commonly tested alternative.
ROS 2 also defines named QoS profiles on top of DDS. The sensor-data profile is best-effort with a shallow history, the services profile is reliable, and the default is reliable with KEEP_LAST(10) and volatile durability. The classic trap is a camera driver publishing with the sensor-data profile while an RViz or recording subscription requests reliable: the offered best-effort is weaker than the requested reliable, so they never match. Tools such as ros2 topic info --verbose expose this.
A newer option is rmw_zenoh_cpp, which implements the RMW interface over Eclipse Zenoh rather than DDS. I believe it was promoted to a supported tier in the Kilted Kaiju release in 2025, but the ROS documentation pages I could reach were blocked during research, so confirm the current tier on docs.ros.org before committing. For a head-to-head view see our ROS 2 DDS vs Zenoh middleware comparison, and for a hands-on bridge, the Zenoh and ROS 2 bridge tutorial.
Where Zenoh fits next to DDS
Zenoh is a pub-sub and query protocol designed to run from microcontrollers to cloud, with routers that forward only subscribed keys. It avoids multicast and multiplies poorly under full-mesh discovery, so it is attractive for Wi-Fi, multi-robot and WAN topologies where DDS simple discovery struggles. The zenoh-bridge-ros2dds project connects a local DDS domain to a Zenoh network, so each robot keeps native DDS internally while only selected topics cross the wireless link. I treat the pairing as complementary: DDS inside the robot or cell, Zenoh between cells.
Real-time tuning: where the milliseconds go
Latency in a DDS system is the sum of serialisation, copy, queueing, network and wake-up costs. The levers, in order of impact:
- Avoid the network when you can. Same-host communication through shared memory removes the kernel UDP stack and, with loaned samples, removes copies. For large fixed-size messages this changes cost from proportional to payload size to nearly constant.
- Use bounded, plain data types. Variable-length strings and unbounded sequences force dynamic allocation and serialisation. Fixed-size structs and bounded sequences enable zero-copy and predictable timing.
- Control threads. Set real-time scheduling priority, CPU affinity and memory locking (
mlockall) for receive and callback threads, and keep discovery threads off the control cores. Most stacks expose thread attributes in XML. - Size socket buffers. A burst of fragments can overflow the default receive buffer, which looks exactly like network loss. Raise
net.core.rmem_maxand tell the DDS stack to request the larger buffer. - Prioritise on the wire. Map
TRANSPORT_PRIORITYto DSCP and priority queues on managed switches.
Do not quote a single latency figure for DDS. Published numbers vary by orders of magnitude with payload size, transport, hardware and QoS. Measure your own system with your own message sizes, and report percentiles (p50, p99, p99.99) rather than averages, because tail latency is what a control loop experiences.

Figure 3: The real-time path of a DDS sample. Shared memory handles same-host traffic, while UDP over a TSN-configured network handles cross-host traffic with scheduled windows.
Time-Sensitive Networking
DDS alone gives you bounded behaviour in the middleware only if the network also behaves. On shared Ethernet a bulk transfer can add milliseconds of queueing delay to a control sample. IEEE 802.1Q Time-Sensitive Networking (TSN) adds time-aware shaping (802.1Qbv), frame preemption (802.1Qbu/802.3br), per-stream filtering and policing (802.1Qci) and time synchronisation (802.1AS) so that a switch can guarantee a transmission window to a priority class. The pattern is to map the DDS control topics to a TSN traffic class using TRANSPORT_PRIORITY or a dedicated transport, and leave bulk topics in best-effort classes. OMG has also developed a DDS-TSN specification for standard configuration; I did not verify its current version for this article, so check the OMG catalogue. Our TSN architecture guide covers the network side in depth.
Working Configuration and Code
An IDL type with keys and extensibility
module fleet {
@appendable
struct VehicleState {
@key string<32> vehicle_id;
double x;
double y;
double speed_mps;
uint64 stamp_ns;
};
};
The bounded string keeps serialisation predictable, the key makes each vehicle an instance with its own history and deadline, and @appendable lets you add fields later without a flag day.
Cyclone DDS configuration
<CycloneDDS xmlns="https://cdds.io/config">
<Domain id="any">
<General>
<Interfaces>
<NetworkInterface name="eth0" multicast="default"/>
</Interfaces>
<AllowMulticast>spdp</AllowMulticast>
</General>
<Discovery>
<ParticipantIndex>auto</ParticipantIndex>
<Peers>
<Peer address="10.0.10.11"/>
<Peer address="10.0.10.12"/>
</Peers>
</Discovery>
<Internal>
<SocketReceiveBufferSize min="4MB"/>
</Internal>
</Domain>
</CycloneDDS>
Load it with export CYCLONEDDS_URI=file:///etc/dds/cyclone.xml. This restricts traffic to one interface, uses multicast only for participant discovery, adds explicit unicast peers for networks where multicast is unreliable, and requests a larger receive buffer. Element names can change between releases, so validate against the Cyclone DDS configuration reference for your version.
A Python writer and reader
from dataclasses import dataclass
from cyclonedds.domain import DomainParticipant
from cyclonedds.topic import Topic
from cyclonedds.pub import DataWriter
from cyclonedds.sub import DataReader
from cyclonedds.core import Qos, Policy
from cyclonedds.idl import IdlStruct
from cyclonedds.idl.annotations import key
from cyclonedds.util import duration
@dataclass
class VehicleState(IdlStruct, typename="fleet::VehicleState"):
vehicle_id: str
x: float
y: float
speed_mps: float
stamp_ns: int
key("vehicle_id")
qos = Qos(
Policy.Reliability.Reliable(duration(milliseconds=100)),
Policy.Durability.TransientLocal,
Policy.History.KeepLast(5),
Policy.Deadline(duration(milliseconds=50)),
)
dp = DomainParticipant(0)
topic = Topic(dp, "VehicleState", VehicleState, qos=qos)
writer = DataWriter(dp, topic, qos=qos)
reader = DataReader(dp, topic, qos=qos)
writer.write(VehicleState("truck-07", 12.5, 3.1, 8.2, 1_700_000_000_000))
for s in reader.take(N=10):
print(s.vehicle_id, s.speed_mps)
This is a sketch assembled from the Cyclone DDS Python binding’s documented style; the exact import paths and key annotation syntax have shifted between releases, so run it against the version you install. The QoS bundle is the interesting part: reliable with a 100 ms blocking bound, transient-local so a late reader gets the last five samples per vehicle, and a 50 ms deadline per vehicle.
Debugging checklist when nothing matches
Run through this order every time. Confirm domain IDs are equal. Confirm multicast works on the interface, or add unicast peers. Confirm the type name and, for XTypes, assignability. Compare reliability, durability and deadline between the two endpoints. Check the incompatible-QoS statuses. Then capture with Wireshark: its RTPS dissector decodes submessages, and filtering on rtps shows whether SPDP announcements leave and arrive. If announcements are visible but no SEDP follows, a firewall or a security governance mismatch is the usual cause.
DDS vs MQTT vs OPC UA PubSub: A Decision Matrix
The old version of this article pitched DDS as the “serious” alternative to MQTT. That framing is too crude. They solve different problems, and a modern plant often uses all three.
| Dimension | DDS | MQTT 5 | OPC UA PubSub |
|---|---|---|---|
| Topology | Peer-to-peer, brokerless | Client to central broker | Brokerless UDP or broker (MQTT, AMQP) |
| Typing | Strongly typed with IDL, keys, XTypes | Opaque payload | Information-model typed |
| QoS | About 20 policies, matched per endpoint | Three delivery levels plus session and expiry | Transport-dependent |
| Discovery | Built in (SPDP and SEDP) | Via broker topic hierarchy | Via OPC UA server or configuration |
| Late joiner state | Writer-side durability | Retained messages | Depends on mapping |
| Typical range | LAN, vehicle, cell | WAN and cloud | Plant floor to edge |
| Strength | Deterministic data sharing with contracts | Simple, scalable, firewall-friendly | Semantic interoperability with equipment |
The decision logic is shown in Figure 4.

Figure 4: A practical protocol selection flow. DDS wins inside the trust domain for typed, time-bounded data; MQTT wins across the WAN; OPC UA wins for equipment semantics.
For plant-to-cloud telemetry, put MQTT or Sparkplug at the boundary, as discussed in our Sparkplug B vs OPC UA PubSub comparison, and keep DDS for the machine-level control plane. If you are choosing for a new robotics or edge-AI system, the DDS vs Zenoh vs MQTT robotics comparison gives the tie-breakers.
Trade-offs, Gotchas, and What Goes Wrong
Discovery storms. The default behaviour of announcing to a multicast group is excellent on a quiet wired LAN and poor on Wi-Fi, in containers with bridged networking, and across VLANs. Symptoms include long start-up times and nodes that appear and vanish. Use static peers, a discovery server or domain partitioning.
Silent QoS incompatibility. Covered above, but worth repeating as the number-one issue. Add a test that asserts every reader matched at least one writer.
Unbounded memory. KEEP_ALL with reliable delivery and a missing reader limit grows without bound until a resource limit blocks the writer. Always set RESOURCE_LIMITS explicitly.
Reliable is not lossless. Reliability is bounded by history. If you need to guarantee processing of every message, you need KEEP_ALL, enough resource, a persistent durability service and application-level acknowledgement. In that regime a message queue or log such as Kafka may be a better fit, and bridging patterns exist; see our MQTT to Kafka bridge tutorial for the cloud-side half.
One slow reader hurts a reliable writer. Per-reader state means a stalled consumer can stall the producer. Isolate bulk consumers or use best-effort for them.
Security surprises. Encrypted topics with unprotected discovery leak topology. Expired certificates produce silent non-matching, which looks like a network fault. Plan certificate rotation and monitor expiry.
Vendor drift. Wire compatibility does not guarantee identical defaults. A fleet mixing Fast DDS and Cyclone DDS nodes will show different discovery timing and sometimes different default QoS in higher-level layers.
Cloud and NAT. Raw RTPS does not traverse NAT or load balancers cleanly. Use a router (Zenoh, RTI Routing Service, Fast DDS TCP transport) rather than forwarding UDP ports.
My second original framing: DDS pushes complexity from the runtime into the design. A broker system fails loudly at runtime when a broker is down. DDS fails quietly at design time when two contracts disagree. Teams that succeed treat QoS as an architectural artefact, review it like an API, and test it in CI.
Practical Recommendations
Start by deciding whether you need DDS at all. If your data crosses a WAN, your consumers are heterogeneous cloud services, or your devices are tiny, choose MQTT, Zenoh or an agent-based XRCE approach and keep DDS local. If you need typed, low-latency, many-to-many sharing with explicit freshness guarantees inside a vehicle, cell or robot fleet segment, DDS is a strong default.
Choose the vendor by constraint, not reputation: Cyclone DDS or Fast DDS for open-source ROS 2 work, RTI Connext when you need certification evidence, vendor support or specialist tooling. Test the exact versions together if you must mix.
Write down a QoS table per topic as a design document, review it, and test it. Prefer KEEP_LAST(1) best-effort for fast-changing state, reliable KEEP_LAST(n) transient-local for configuration and commands, and reserve KEEP_ALL for deliberate logging.
Checklist before production:
- Domain IDs and discovery scope documented, with static peers or a discovery server on any Wi-Fi or routed segment.
- QoS table per topic reviewed, with a CI test asserting that every reader matches.
- Deadline and liveliness set on every control-critical topic, with handlers that trigger a safe state.
- Types marked appendable or mutable, with a versioning policy.
- DDS Security enabled in staging, with governance protecting discovery and a certificate-expiry alarm.
- Latency measured as percentiles with production message sizes, shared memory enabled for same-host traffic.
- Socket buffers, thread priorities and (where needed) TSN traffic classes configured and tested under load.
- Wireshark capture procedure and incompatible-QoS monitoring included in the runbook.
Frequently Asked Questions
What is the DDS data distribution service used for?
DDS is used where many components must share typed, time-sensitive data with explicit delivery guarantees and without a central broker. Common domains include defence and aerospace systems, air traffic control, autonomous vehicles, medical devices, industrial automation cells and robotics through ROS 2. Its strengths are peer-to-peer discovery, per-endpoint QoS contracts and deterministic behaviour on a local network. It is less suited to carrying telemetry over the public internet or to very small devices without a gateway.
Is DDS the same as ROS 2?
No. DDS is an OMG standard for data-centric middleware. ROS 2 is a robotics framework that uses a middleware abstraction layer, the RMW interface, to run on top of DDS implementations such as Fast DDS, Cyclone DDS and Connext. ROS 2 adds its own message definitions, naming conventions, services, actions and tools. Newer ROS 2 releases can also run over a non-DDS middleware based on Zenoh, so DDS knowledge helps but is not the whole story.
What is the difference between DDS and RTPS?
DDS defines the application programming model: participants, topics, writers, readers and QoS. RTPS, formally DDSI-RTPS, defines the on-the-wire protocol that implementations use to discover each other and exchange samples, so that products from different vendors interoperate. In short, DDS is the API and behaviour, RTPS is the protocol. RTPS version 2.5, published in April 2022, is the latest formal version listed by OMG.
Is DDS better than MQTT?
Neither is universally better. DDS offers brokerless discovery, strong typing and fine-grained QoS, which suits real-time control and sensor fusion inside one trust domain. MQTT offers simplicity, firewall-friendly client-to-broker connections and wide cloud support, which suits telemetry across wide-area networks. Many architectures use DDS locally and bridge selected topics to MQTT or Kafka at the edge gateway. The right question is where each belongs in your topology.
Does DDS work over Wi-Fi or the internet?
It can, but with care. Default multicast discovery performs poorly on Wi-Fi and is usually blocked across routers and clouds. Use unicast static peers, a discovery server, or a router or bridge such as Zenoh or RTI Routing Service to carry selected topics over wireless or wide-area links. Avoid sending large reliable samples over lossy radio links, and consider best-effort QoS for streams where fresh data supersedes old data.
How do I secure a DDS system?
Use the DDS Security specification, now at version 1.2. Issue X.509 certificates from a certificate authority, sign a governance document that sets domain-wide protection rules and a permissions document per participant, and enable the AES-GCM cryptographic plugin. Protect discovery as well as data, or topic names leak. In ROS 2, SROS 2 automates keystore generation. Budget CPU for encryption and plan certificate rotation.
Further Reading
- DDS vs MQTT vs OPC UA: industrial messaging protocols in 2026
- ROS 2 DDS vs Zenoh robotics middleware comparison
- Time-Sensitive Networking IEEE 802.1Q architecture guide
- Zenoh and ROS 2 bridge tutorial
- OMG specification catalogue: DDSI-RTPS, DDS Security, DDS-XTypes
- Eclipse Cyclone DDS on GitHub
By Riju — about
