ROS 2 command line cheat sheet
The ros2 topic, node, service, param, bag, action, interface, launch, run, pkg and doctor commands for ROS 2 Jazzy, with QoS flags, a runnable QoS mismatch demo and the gotchas that waste the most time.
This sheet is for anyone debugging a running ROS 2 system from a terminal. Every command and flag below was checked against ros2 <command> -h in the ros:jazzy-ros-base container on 14 November 2026, and against the Jazzy documentation. The target is ROS 2 Jazzy Jalisco (LTS, May 2024). Lyrical Luth (LTS, May 2026) adds options, for example to ros2 param get, so check -h on newer distributions. New to ROS 2? Start with hello ROS 2 and the glossary entries node, topic, service, action and QoS.
Before anything else
source /opt/ros/jazzy/setup.bash # every new shell, or add it to ~/.bashrc
source install/setup.bash # your workspace, after colcon build
printenv | grep -E 'ROS_(DISTRO|DOMAIN_ID|AUTOMATIC_DISCOVERY_RANGE)|RMW_IMPLEMENTATION'
ros2 doctor --report | less # platform, network, middleware and package checks
Topics
ros2 topic list -t # names with types
ros2 topic info /scan -v # publishers, subscribers and the QoS of each
ros2 topic echo /odom --once # one message, then exit
ros2 topic echo /odom --field pose.pose.position
ros2 topic echo /scan --no-arr # hide long arrays
ros2 topic echo /joint_states --csv > joints.csv
ros2 topic hz /camera/image_raw # rate, averaged over a window
ros2 topic bw /camera/image_raw # bandwidth
ros2 topic delay /scan # age of messages, from header.stamp
ros2 topic find sensor_msgs/msg/Imu
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.1}, angular: {z: 0.0}}"
ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.1}}" # 10 Hz until Ctrl-C
ros2 topic pub -t 5 -w 1 /chatter std_msgs/msg/String "{data: hello}" # 5 messages, after 1 subscriber matches
Nodes
ros2 node list
ros2 node list -a # include hidden nodes
ros2 node info /controller_manager # its topics, services and actions
Services
ros2 service list -t
ros2 service type /add_two_ints
ros2 service find std_srvs/srv/Trigger
ros2 service call /reset std_srvs/srv/Empty
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 2, b: 3}"
ros2 service echo /add_two_ints # needs service introspection enabled on the client or server
Parameters
ros2 param list /camera_driver
ros2 param get /camera_driver exposure
ros2 param set /camera_driver exposure 20
ros2 param describe /camera_driver exposure
ros2 param dump /camera_driver > camera.yaml
ros2 param load /camera_driver camera.yaml
ros2 param delete /camera_driver debug_mode
Bags
The default storage format in Jazzy is MCAP. In Jazzy, topics are passed with --topics, and the bare positional list is deprecated.
ros2 bag record -a -o run_01 # all topics and services
ros2 bag record --topics /tf /tf_static /odom /scan -o nav_test
ros2 bag record -e '/camera/.*' -o cams # regex
ros2 bag record --topics /scan -d 60 -o split_bag # new file every 60 s
ros2 bag info nav_test
ros2 bag play nav_test -r 0.5 # half speed
ros2 bag play nav_test --clock -l # publish /clock and loop
ros2 bag play nav_test --topics /scan --start-offset 10
When you play a bag with --clock, run the nodes under test with use_sim_time set to true, or their timestamps will not match the data.
Actions
ros2 action list -t
ros2 action info /navigate_to_pose
ros2 action type /navigate_to_pose
ros2 action send_goal -f /fibonacci example_interfaces/action/Fibonacci "{order: 5}" # -f prints feedback
Interfaces
ros2 interface list
ros2 interface packages
ros2 interface package sensor_msgs
ros2 interface show geometry_msgs/msg/Quaternion # fields x, y, z, w
ros2 interface proto geometry_msgs/msg/Twist # a YAML template to paste into pub or call
Run and launch
ros2 run demo_nodes_cpp talker
ros2 run demo_nodes_cpp talker --ros-args -r __node:=talker_2 -r chatter:=chatter_2
ros2 run demo_nodes_cpp parameter_blackboard --ros-args -p a_string:=hello --params-file demo_params.yaml
ros2 run demo_nodes_cpp talker --ros-args --log-level debug
ros2 run --prefix 'gdb -ex run --args' demo_nodes_cpp talker
ros2 launch nav2_bringup tb3_simulation_launch.py --show-args
ros2 launch my_robot bringup.launch.py use_sim_time:=true
ros2 launch my_robot bringup.launch.py --print # show the launch description without running it
Packages and the daemon
ros2 pkg list
ros2 pkg executables demo_nodes_cpp
ros2 pkg prefix rclpy
ros2 pkg create my_pkg --build-type ament_python --dependencies rclpy std_msgs --node-name my_node
ros2 pkg xml my_pkg
ros2 daemon stop && ros2 daemon start # clears a stale discovery cache
ros2 doctor hello # check that hosts see each other
QoS flags
ros2 topic echo and ros2 topic pub take the same QoS options.
| Flag | Values |
|---|---|
--qos-profile |
default, system_default, sensor_data, services_default, parameters, parameter_events, action_status_default, best_available |
--qos-reliability |
reliable, best_effort, system_default, best_available |
--qos-durability |
volatile, transient_local, system_default, best_available |
--qos-history, --qos-depth |
keep_last with a depth, or keep_all |
The help text in Jazzy says that, by default, echo and pub pick reliability and durability compatible with the endpoints already running. You need the flags when the other side starts later, or when you want to test a specific profile.
ros2 topic echo /map --qos-durability transient_local --qos-reliability reliable
ros2 topic pub /scan_status std_msgs/msg/String "{data: ok}" --qos-reliability best_effort
The compatibility rules from the Jazzy QoS page:
| Publisher | Subscriber | Connects |
|---|---|---|
| Best effort | Best effort | Yes |
| Best effort | Reliable | No |
| Reliable | Best effort | Yes |
| Reliable | Reliable | Yes |
| Volatile | Transient local | No |
| Transient local | Volatile | Yes, new messages only |
| Transient local | Transient local | Yes, including the stored history |
This block shows the second row in practice. It runs in the Jazzy container: one best-effort publisher, one reliable and one best-effort subscriber.
# ros2
import rclpy
from rclpy.qos import QoSProfile, ReliabilityPolicy
from std_msgs.msg import String
rclpy.init()
node = rclpy.create_node("qos_check")
best_effort = QoSProfile(depth=10, reliability=ReliabilityPolicy.BEST_EFFORT)
reliable = QoSProfile(depth=10, reliability=ReliabilityPolicy.RELIABLE)
received = {"reliable_sub": 0, "best_effort_sub": 0}
pub = node.create_publisher(String, "scan_status", best_effort)
node.create_subscription(String, "scan_status", lambda m: received.__setitem__("reliable_sub", received["reliable_sub"] + 1), reliable)
node.create_subscription(String, "scan_status", lambda m: received.__setitem__("best_effort_sub", received["best_effort_sub"] + 1), best_effort)
for i in range(40):
pub.publish(String(data=f"msg {i}"))
rclpy.spin_once(node, timeout_sec=0.05)
print("best-effort publisher ->", received) # the reliable subscription stays at 0
node.destroy_node()
rclpy.shutdown()
Output on ROS 2 Jazzy, with the timestamp and the matching publisher-side warning removed:
[WARN] [qos_check]: New subscription discovered on topic 'scan_status', requesting incompatible QoS. No messages will be sent to it. Last incompatible policy: RELIABILITY
best-effort publisher -> {'reliable_sub': 0, 'best_effort_sub': 38}
A late subscriber to a latched-style topic such as /map or /robot_description gets nothing unless both sides use transient local durability.
Gotchas
| Symptom | Cause | Fix |
|---|---|---|
ros2 topic list shows nothing from the other machine |
Different ROS_DOMAIN_ID, discovery limited by ROS_AUTOMATIC_DISCOVERY_RANGE, or a firewall blocking multicast |
Match the domain ID on both machines, check the discovery range, run ros2 doctor hello on both |
| A node you killed still appears in the list | The CLI daemon caches discovery | ros2 daemon stop, then retry |
echo shows nothing, info -v shows both ends |
QoS mismatch | Compare the QoS in ros2 topic info -v, then use the flags above |
Package 'x' not found right after a build |
Workspace not sourced in this shell | source install/setup.bash |
| TF lookups fail during bag playback | Nodes use wall time, bag has old stamps | ros2 bag play --clock and use_sim_time:=true |
ros2 param set returns but nothing changes |
The node reads the parameter only at start-up | Restart the node, or add a parameter callback in the code |
pub messages vanish |
The publisher exits before discovery completes | Use -w 1, or -t N with a rate |
| Two simulators on one network fight over topics | Same domain on a shared network | Give each robot or developer its own ROS_DOMAIN_ID |
Related pages
- Hello ROS 2 and the talker, listener and tf2 project
- ROS and middleware hub and the ROS platform page
- Software matrix for distribution versions and support dates
- Jazzy docs: topics, bags, node arguments, environment, ros2doctor