Two ROS 2 nodes on opposite ends of a VPN can list each other's topics and still fail to deliver a single message. Discovery and user data are separate exchanges with separate paths, and a discovery server does not relay payloads. Checking one and assuming the other is how teams end up replacing middleware when the tunnel simply never carried the data. The runbook below separates the two results before anything gets swapped.

What the discovery server actually carries

In Fast DDS a Discovery Server distributes information about endpoints: who publishes, who subscribes and what QoS each expects. Once peers know about each other, user data travels directly between them rather than through that server. Reaching the server proves that the broker of introductions answers, nothing more. A routed VPN still needs a working path in both directions between the addresses the peers advertise.

That distinction also explains partial failures, where existing peers keep exchanging data while new discovery stops working. Treat those as two investigations.

Read the RMW the nodes are really using

ROS 2 can load different middleware, and a variable exported in your shell does not reach processes started somewhere else. Ask the running system instead of your terminal history.

printenv RMW_IMPLEMENTATION
ros2 doctor --report | head -n 20

An unset variable usually means the installed default, which can differ between machines in the same experiment. The report also names the distribution and the middleware in use, so compare it on both sides of the tunnel before assuming they match. The ROS command-line daemon caches state, so restart it after changing the environment rather than letting a stale daemon answer for the old configuration. Put the distribution and RMW of both hosts into the test notes while the session is open.

Test discovery and data as two results

Pick a harmless topic and measure introduction separately from delivery. The info command lists endpoints and QoS, while echo proves whether one message actually crosses the link.

ros2 topic info /chatter --verbose
ros2 topic echo /chatter --once

Endpoints showing up in the info output is a discovery result. A message arriving at the subscriber is a transport result, and only the second one exercises the path the application needs. When discovery works and echo times out, inspect the advertised addresses and the return route instead of the discovery service. Run both commands from each host, because an asymmetric VPN route often reveals itself from one side only.

Change one dependency at a time

When the data path fails, avoid changing middleware, discovery mode and message size in the same session, since no result can be attributed afterwards. The Zenoh RMW is a legitimate alternative, but its router becomes another dependency that must be reachable through the tunnel.

export RMW_IMPLEMENTATION=rmw_zenoh_cpp
ros2 run rmw_zenoh_cpp rmw_zenohd

Under that RMW the router carries discovery in the default Jazzy arrangement, and multicast discovery is disabled there, so the dependency belongs in your notes rather than in an assumption borrowed from another middleware. Keep the workload fixed while you change one setting, then repeat the echo test and record direction, message rate and link conditions. Run the comparison twice to separate a real improvement from a lucky reconnect. If a change improves discovery while delivery stays the same, you have moved the introduction service, not the data path.

Artiglyn mynjee