How to persist rosdep dependencies and build artifacts across container restarts in Isaac ROS 3.2 Workflow?

Hi Isaac ROS Team,

I am currently following the Isaac Manipulator End-to-End Workflow Tutorial (release-3.2): https://nvidia-isaac-ros.github.io/v/release-3.2/reference_workflows/isaac_manipulator/tutorials/tutorial_e2e.html

Every time I restart the dev container using ./scripts/run_dev.sh, I notice that the environment resets, and I am forced to re-run the following setup steps every single time:

sudo apt-get update
rosdep update && \
rosdep install -i -r \
  --from-paths ${ISAAC_ROS_WS}/src/isaac_manipulator/isaac_manipulator_bringup/ \
  --rosdistro humble -y

# Build and source
cd ${ISAAC_ROS_WS}
colcon build --symlink-install --packages-up-to isaac_manipulator_bringup
source install/setup.bash

Since run_dev.sh uses the --rm flag, all apt packages installed inside the container are wiped upon exit. Is there an officially recommended way to avoid downloading and installing these dependencies repeatedly on every restart?

Hello @chihyungwang79,

Thanks for posting in the Isaac ROS forum!

This is expected behavior for the Isaac ROS dev container. run_dev.sh creates a disposable container, so packages installed with apt or rosdep inside the running container are lost when the container is removed.

If the dependency is available as source, another good option is to clone it into ${ISAAC_ROS_WS}/src and build it with colcon build. Since ${ISAAC_ROS_WS} is bind-mounted from the host into the container, the source tree and the generated build/, install/, and log/ directories will persist across container restarts.

For normal use, after restarting the container you should only need:
cd ${ISAAC_ROS_WS}
source install/setup.bash

If you really want the packages installed by apt to remain after the container is removed, you can commit the running container to a new Docker image: docker commit. Then start future containers from that committed image.
However, this is usually better treated as a quick local workaround rather than the recommended workflow, because the changes are not captured in a Dockerfile and are harder to reproduce or share.

Hi, thanks for the quick reply!

Regarding the suggestion of cloning dependencies into ${ISAAC_ROS_WS}/src, I think it might be impractical for my current setup because isaac_manipulator_bringup relies heavily on almost the entire Isaac ROS ecosystem.

Here is the dependency list from my package.xml:

<depend>isaac_manipulator_ros_python_utils</depend>
  <depend>isaac_ros_cumotion</depend>
  <depend>isaac_ros_cumotion_examples</depend>
  <depend>isaac_ros_cumotion_interfaces</depend>
  <depend>isaac_ros_cumotion_moveit</depend>
  <depend>isaac_ros_cumotion_object_attachment</depend>
  <depend>isaac_ros_dope</depend>
  <depend>isaac_ros_esdf_visualizer</depend>
  <depend>isaac_ros_ess</depend>
  <depend>isaac_ros_foundationpose</depend>
  <depend>isaac_ros_hawk</depend>
  <depend>isaac_ros_image_proc</depend>
  <depend>isaac_ros_launch_utils</depend>
  <depend>isaac_ros_moveit_goal_setter</depend>
  <depend>isaac_ros_nitros_topic_tools</depend>
  <depend>isaac_ros_pose_proc</depend>
  <depend>isaac_ros_rtdetr</depend>
  <depend>isaac_ros_tensor_proc</depend>
  <depend>nvblox_ros</depend>
  <depend>nvblox_examples_bringup</depend>
  <depend>ros2launch</depend>
  <depend>tf2_ros</depend>
  <depend>ur_moveit_config</depend>

Cloning all of these heavy NVIDIA packages (like cuMotion, nvblox, FoundationPose, etc.) from source into src/ and building them locally would be overwhelming and might cause hardware-accelerated compilation complexities.

Given this heavy dependency on binary packages, what would be the officially recommended way to persist these apt/rosdep dependencies?

Should I add these steps into Dockerfile.user to bake them into the image permanently so that I can develop offline without re-downloading them every time the container restarts?

Thanks again for your guidance!

For a binary-heavy workflow, you could choose to put the apt/rosdep dependencies in a custom Docker layer. Docker commit can work as a quick local workaround, but the Dockerfile layer is more reproducible. You could keep only the packages you are actively developing in
${ISAAC_ROS_WS}/src, and bake the binary apt/rosdep dependencies into a custom Isaac ROS dev image layer.
The release-3.2 docs describe adding custom Dockerfile layers through .isaac_ros_common-config, CONFIG_IMAGE_KEY, and CONFIG_DOCKER_SEARCH_DIRS.

An example setup flow would be:

mkdir -p ${ISAAC_ROS_WS}/docker_custom

cat > "${ISAAC_ROS_WS}/docker_custom/Dockerfile.manipulator" <<'EOF'
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

RUN apt-get update && apt-get install -y --no-install-recommends \
    ros-humble-isaac-manipulator-bringup \
    && rm -rf /var/lib/apt/lists/*
EOF
#Replace ros-humble-isaac-manipulator-bringup with the apt packages you actually want to persist.

# Then create or edit:
~/.isaac_ros_common-config

with:

CONFIG_IMAGE_KEY="ros2_humble.manipulator"
CONFIG_CONTAINER_NAME_SUFFIX="manipulator"
CONFIG_DOCKER_SEARCH_DIRS=("/mnt/nova_ssd/workspaces/isaac_ros-dev/docker_custom")

Then rebuild/start:

cd ${ISAAC_ROS_WS}/src/isaac_ros_common/scripts
./run_dev.sh -d ${ISAAC_ROS_WS}

After the image is built once, future starts can use:

./run_dev.sh -d ${ISAAC_ROS_WS} -b

Hi, thank you for this elegant solution! The custom Docker layer workflow via ~/.isaac_ros_common-config is exactly what I was looking for.

However, I encountered a practical limitation when trying to implement this. Prior to configuring the manipulator setup, I had already followed the official Isaac ROS RealSense hardware setup guide(Isaac ROS RealSense Setup — isaac_ros_docs documentation), which required setting: CONFIG_IMAGE_KEY="ros2_humble.realsense"

Since CONFIG_IMAGE_KEY only accepts a single string value in run_dev.sh, creating the new ros2_humble.manipulator layer completely overwrites the previous RealSense configuration. As a result, components like realsense-viewer and related dependencies disappear when the manipulator layer is active.

You are right that CONFIG_IMAGE_KEY is only one variable, but it does not need to represent only one Docker layer. Isaac ROS image keys are dot-delimited layer chains.

You could use a combined key like this:
CONFIG_IMAGE_KEY="ros2_humble.realsense.manipulator"

With this, the image is composed as:
Dockerfile.ros2_humble
Dockerfile.realsense
Dockerfile.manipulator
so the custom manipulator layer is built on top of the RealSense layer, and tools/dependencies from the RealSense setup, such as realsense-viewer, should remain available.

Run without -b the first time so the combined image is built and after that, use -b to reuse the existing image.