Moving from ROS 2 Humble to Lyrical Luth: An Upgrade Checklist for Production Systems
ROS 2 Humble Hawksbill reaches end-of-life in May 2027, according to the official distribution table. The current long-term support release, Lyrical Luth, came out on 22 May 2026 and is supported until May 2031. Moving straight from Humble to Lyrical crosses Iron, Jazzy and Kilted, and each of those releases removed or renamed something. A phased approach of audit, port, then validate keeps a production upgrade predictable.
This checklist is written for teams that maintain ROS 2 robots under a service contract: system integrators, automation suppliers and university labs running shared research platforms. Every change listed is taken from the release notes and guides in the ROS 2 documentation (Lyrical branch), which you can search in the ROS 2 documentation base.
Why the calendar should drive the plan
From the date of this article (5 October 2026) to Humble's end-of-life in May 2027 is roughly seven months. The Lyrical release timeline spells out what end-of-life means: the distribution stops receiving updates, including security updates. For a robot sold into a factory or a hospital, that is hard to justify.
- Kilted Kaiju (released 23 May 2025) ends in December 2026, so it is not worth targeting.
- Jazzy Jalisco (released 23 May 2024) is supported until May 2029.
- Lyrical Luth (released 22 May 2026) is supported until May 2031.
- Makoa Mata-mata, planned for May 2027, is described as a regular release supported until December 2028.
Phase 1: audit what you are running
Start with an inventory. Three findings usually shape the rest of the project.
Operating system
Humble's Tier 1 platform was Ubuntu 22.04 (Jammy). Lyrical's Tier 1 platforms are Ubuntu Resolute (26.04) on amd64 and arm64 and Windows 11 (VS2022) on amd64. RHEL 10 is Tier 2. Ubuntu Noble (24.04) and Debian Trixie are only Tier 3, with early EOLs (Noble on 2029-06-01, Trixie on 2028-08-09), and Tier 3 means building Lyrical from source. Lyrical's minimum language requirements are C++20, C17 and Python 3.12 to 3.14.
Middleware
The default remains rmw_fastrtps_cpp. In Lyrical, Fast DDS, Cyclone DDS and Zenoh are Tier 1 on all architectures. Connext is Tier 1 on all architectures except arm64. If you use Fast DDS XML profiles, note that Kilted renamed fastrtps to fastdds. The rmw names stay the same, but the XML profile environment strings change.
Environment variables
Search your launch scripts and service files for ROS_LOCALHOST_ONLY. Iron deprecated it in favour of ROS_AUTOMATIC_DISCOVERY_RANGE (SUBNET by default, or LOCALHOST, OFF and SYSTEM_DEFAULT) together with ROS_STATIC_PEERS, a semicolon-separated list of addresses.
Phase 2: port the code
Breaking or deprecating changes between Humble and Lyrical
| Release | Change | Action |
|---|---|---|
| Iron | RCLCPP_SCOPE_EXIT removed | Use RCPPUTILS_SCOPE_EXIT |
| Iron | rcutils/get_env.h removed | Include rcutils/env.h |
| Iron | RosTimer and PushRosNamespace renamed | Use ROSTimer and PushROSNamespace |
| Iron | LaunchConfigurationEquals / NotEquals deprecated | Use the Equals and NotEquals substitutions |
| Jazzy | Subscription callbacks taking std::shared_ptr<MessageT> removed | Take std::shared_ptr<const MessageT> |
| Jazzy | tf2_*.h headers removed (tf2_eigen, tf2_geometry_msgs, tf2_kdl, tf2_sensor_msgs, tf2_bullet) | Include the .hpp headers |
| Jazzy | rclcpp/qos_event.hpp removed | Include rclcpp/event_handler.hpp |
| Jazzy | rclcpp::get_typesupport_handle deprecated | Use rclcpp::get_message_typesupport_handle |
| Jazzy | rosbag2 --exclude renamed | Use --exclude-regex |
| Kilted | ament_target_dependencies() deprecated | Use target_link_libraries() with modern CMake targets |
| Lyrical | uint8[] fields become rosidl::Buffer<uint8_t> in C++ | Review code that assumed std::vector<uint8_t> |
The CMake change catches people out. The Kilted deprecation warning proposes a target_link_libraries() call with a scope keyword such as PUBLIC. If the same target already uses the plain form, which some ament_cmake macros like ament_add_gtest do internally, remove the keyword. Otherwise CMake reports that all uses must be either all-keyword or all-plain.
In Python, Jazzy changed rclpy.node.Node.declare_parameter so that it no longer allows statically typing a parameter without a default value. Check any node that declares parameters this way.
Phase 3: validate behaviour, not just the build
Several changes compile cleanly but alter how the system runs. Add these to your acceptance tests:
- Executor threading: since Iron, the multi-threaded executor defaults to the number of CPUs on the machine, or 2 if the OS cannot report it.
- Callback ordering: since Jazzy, callbacks in the default executors are no longer ordered consistently, even within the same entity.
- Logging: since Iron, the spdlog backend flushes on each error message and every five seconds.
- Bags: since Iron, rosbag2 writes mcap by default. sqlite3 remains selectable and both formats play back.
- Environment check: printenv | grep -i ROS should show ROS_DISTRO=lyrical, and ros2 doctor --report now also lists actions, services and ROS environment variables.
- QoS on services: if clients stop getting responses after the upgrade, ros2 service info --verbose (new in Lyrical) prints the QoS profile of each request reader and response writer, much as ros2 topic info --verbose does for topics.
- Recorded data: replay a few representative bags from the Humble system through the upgraded nodes. Jazzy changed how offered_qos_profiles are represented in bag metadata and override files, which now use human-readable values.
For a production system it also helps to pin exactly what was tested. The documentation describes apt snapshots for long-term support releases on Ubuntu. These let you install packages as they were in the main repository at the time of the snapshot, for example with sudo apt install ros-lyrical-desktop once the snapshot source is configured. The docs warn that the ROSDISTRO_INDEX_URL environment variable takes precedence over the ~/.config/rosdistro/config.yaml pin, so unset it if it is defined. They also note that moving an existing installation onto a snapshot with sudo apt dist-upgrade may downgrade packages. Two further warnings matter for maintenance contracts. Snapshots receive no bug fixes or security updates. And when a distribution reaches end-of-life, a last snapshot named final is taken, which will be the frozen state of Humble after May 2027.
What to put in the handover note
- The ROS distribution and Ubuntu release on every machine, with the RMW implementation in use.
- The discovery settings (ROS_DOMAIN_ID, ROS_AUTOMATIC_DISCOVERY_RANGE, ROS_STATIC_PEERS) for each site.
- Any QoS overrides used for recording, since mcap is now the default bag format.
- The executor chosen for each node, if you move to the Lyrical EventsCBGExecutor, which the release notes say uses 10% to 15% less CPU than the single- and multi-threaded executors.
Cut over whole systems
The documentation states that nodes are not guaranteed to communicate across distributions. It gives Humble and Iron as an example, saying it may or may not work but is not supported. Upgrade a robot, its operator workstation and any monitoring PC together, and keep the same RMW implementation on all of them.
Leftovers from earlier ROS 1 ports
Codebases that started life in ROS 1 often carry old build metadata, and an LTS upgrade is a good moment to clean it up. ROS 2 requires package.xml format 2 or higher, and colcon follows REP 149, with format 2 also supported. The run_depend tag is no longer allowed. Replace it with exec_depend if the dependency is needed at run time, and/or build_export_depend if packages that build against yours need it. Use both if unsure. When build_depend, build_export_depend and exec_depend all name the same package, a single depend tag replaces them.
Remember also that a colcon workspace creates build, install and log directories next to src, and that, compared to catkin, there is no devel directory. Scripts that still look for devel/setup.bash will fail silently on the new build server.
Keep the release notes one question away
Kopik's ROS 2 base indexes the official release notes of every distribution alongside the Lyrical Luth concepts, tutorials and migration guides. Ask “When does Kilted Kaiju reach end-of-life, and is that before or after the current Lyrical Luth LTS?” and the answer cites the passage it comes from.
Integrators who maintain their own upgrade runbooks can also publish them as a private base from the knowledge base builder. The ROS 2 base covers the core documentation only, so check third-party packages such as Nav2 or MoveIt 2 against their own release notes.
Frequently asked questions
Is ROS 2 Humble still supported in late 2026?
Yes. The official table lists Humble Hawksbill, released on 23 May 2022, with an end-of-life date of May 2027.
What is the difference between Lyrical Luth and Makoa Mata-mata?
Lyrical Luth is a long-term support release supported until May 2031. Makoa Mata-mata, planned for May 2027, is described in the documentation as a regular release supported until December 2028.
Do I have to move to Ubuntu 26.04 for ROS 2 Lyrical?
For Tier 1 binary support, yes. Ubuntu Resolute (26.04) is Tier 1, whereas Ubuntu Noble (24.04) is Tier 3 with an early end-of-life of 2029-06-01, and Tier 3 platforms require building Lyrical from source.
What replaces ROS_LOCALHOST_ONLY?
Since Iron, ROS_LOCALHOST_ONLY is deprecated in favour of ROS_AUTOMATIC_DISCOVERY_RANGE (SUBNET, LOCALHOST, OFF or SYSTEM_DEFAULT) and ROS_STATIC_PEERS for connecting to specific machines.
Is ament_target_dependencies still usable in Lyrical?
It was deprecated in Kilted Kaiju. The Kilted release notes say the macro still works but emits a CMake deprecation warning recommending target_link_libraries() with modern CMake targets. The Lyrical new-features page does not announce its removal, but migrating now avoids the warning.
Get the Kopik newsletter
New knowledge bases, RAG guides and product news. One email every week or two, unsubscribe in one click.
By subscribing you agree to receive our newsletter. We never share your address.