Documentation
ROS 2 over Tailscale: why DDS discovery fails
You connected two ROS 2 machines to the same Tailscale network, ping works, and yet “ros2 topic list” shows nothing from the other side. It is not a bug in your setup: DDS, the middleware under ROS 2, discovers its peers over local multicast — and a WireGuard mesh routes unicast, not multicast. Here is why, the classic DDS workarounds and their limits, and the “broadcast domain” approach that gives multicast its role back.
The symptom: connected, yet invisible to each other
The scenario is always the same. Two machines — a robot in the field, a supervision workstation at the office — join the same overlay network. Each one sees the other’s address, ping answers, SSH works. So the network is up.
Then you start your ROS 2 nodes on both sides, with the same ROS_DOMAIN_ID, and nothing : ros2 topic list only shows local topics, ros2 node list ignores the remote nodes, no message flows. The two ROS 2 graphs each live in their own bubble, as if the other machine did not exist.
The explanation: DDS discovers over multicast, the overlay routes unicast
ROS 2 does not talk to the network directly : it delegates to DDS, its publish/subscribe middleware. And DDS finds its participants through the standard SPDP protocol (Simple Participant Discovery Protocol) : each participant periodically announces its presence on a well-known UDP multicast group of the local segment. Whoever hears those announcements joins the graph ; whoever does not hear them does not exist.
A WireGuard mesh like Tailscale, however, is a layer-3 overlay routed in unicast : it carries IP packets from one identified machine to another identified machine. According to its public documentation, broadcast and IP multicast are not natively supported. The SPDP announcements sent to the multicast group therefore leave on the local interface… and stop there : they never cross the tunnel.
The classic DDS workarounds and their limits
The DDS ecosystem is not helpless : since multicast discovery fails, it can be replaced with explicit unicast discovery. Every variant boils down to the same idea : telling each participant, in advance, where to find the others.
- Static peer list. Most DDS implementations accept a list of peer addresses to contact over unicast (depending on the implementation : environment variable, XML profile file…). Every machine has to know the addresses of all the others.
- Discovery server. Some stacks offer a central discovery server that all participants contact over unicast. Discovery works, but it is no longer peer-to-peer : the server becomes a component to host, reach and keep alive.
These mechanisms are legitimate and documented by DDS vendors. Their limit is operational : the configuration lives machine by machine, depends on the DDS implementation in use (and can differ between robots of the same fleet), and must be revised every time the fleet changes — one robot added, one address changed, and the peer list of every machine needs updating. What was zero-configuration in the lab becomes an inventory to keep current.
The broadcast-domain approach: giving multicast its role back
The alternative is to treat the cause rather than the symptom : if discovery fails because multicast does not traverse, make it traverse. VIGIL-MESH replicates IP multicast and broadcast across its encrypted overlay : for diffusion, the network behaves like a single switch. SPDP announcements sent on one machine reach the members subscribed to the group, wherever they are — and DDS discovery works the way it does in the lab, with no peer list and no discovery server to maintain.
- Zero DDS-specific configuration. Multicast subscriptions are detected automatically (local IGMPv3/MLDv2 querier) ; the DDS stack does not need to know it is not on a LAN.
- End-to-end encrypted. Replicated multicast travels under sender keys that the relay replicates without being able to read.
- Bounded. A two-stage storm control caps diffusion — something to size on large DDS graphs, detailed in the advanced multicast page.