ROS 2’s DDS layer handles peer discovery automatically — nodes find each other without a central master. That works beautifully when it works, and is mysteriously broken when it doesn’t. This page covers the four most common reasons two ROS 2 nodes can see each other’s published topics but fail to subscribe successfully, or fail to see each other at all.
Cause 1 — ROS_DOMAIN_ID mismatch
ROS_DOMAIN_ID is the network namespace for DDS. Nodes with different domain IDs cannot see each other, by design. The default is 0; you can set it to any integer 0-101.
Diagnostic: echo $ROS_DOMAIN_ID on every machine / shell where you’re running nodes. They must all match.
Fix: export the same ROS_DOMAIN_ID in every shell. In ~/.bashrc or a project-local .env file.
Cause 2 — Firewall blocking multicast / UDP
DDS uses multicast UDP for initial discovery and unicast UDP for ongoing communication. A firewall that blocks UDP on the relevant ports breaks discovery.
Diagnostic: sudo ufw status or sudo iptables -L. Look for rules blocking 7400-7500 (DDS default port range).
Fix: open the ports, or disable the firewall on the dev network. For a dev machine on a trusted LAN, sudo ufw disable is usually fine.
Cause 3 — Multiple network interfaces
If your machine has multiple NICs (wired + WiFi + a Docker bridge + tailscale + …), DDS may pick the wrong one for discovery — broadcasting on docker0 instead of eth0, for example.
Diagnostic: ros2 topic list shows topics from local node but not from a remote node on the same LAN; tcpdump -i any port 7400 shows traffic on the wrong interface.
Fix: restrict DDS to one interface. With CycloneDDS:
export CYCLONEDDS_URI='<CycloneDDS><Domain><General><NetworkInterfaceAddress>eth0</NetworkInterfaceAddress></General></Domain></CycloneDDS>'
For development on a single machine, the simpler fix is export ROS_LOCALHOST_ONLY=1 — DDS will skip discovery on external interfaces entirely.
Cause 4 — Stale shared-memory segments
DDS uses shared memory for fast intra-machine communication. After a crashed node, stale /dev/shm/fastrtps_* segments can confuse discovery.
Diagnostic: ls /dev/shm/ | grep fastrtps shows old segments.
Fix:
sudo rm /dev/shm/fastrtps_*
# kill any zombie ROS processes first:
pkill -9 -f 'rclpy|rclcpp|ros2'
This is also a useful step when discovery has been working and suddenly stops — usually means a previous crash left state behind.
A diagnostic recipe
When in doubt, run this sequence on two machines you expect to see each other:
# On machine A
ros2 daemon stop && ros2 daemon start
ros2 topic list
ros2 multicast send
# On machine B
ros2 multicast receive
If multicast receive doesn’t see the send, you have a network / firewall / interface problem. If it does, you have a ROS_DOMAIN_ID or shared-memory problem.
Where to go next
- ROS 2 latency tuning — the deeper config side of DDS
- Troubleshooting hub — sibling pages
- venv ↔ colcon conflict — adjacent environment-conflict issue