Running Isaac Lab in Docker#

Docker packages Isaac Lab and its dependencies into a reusable environment. docker/container.py is the main entry point for running Isaac Lab in a container. It wraps docker compose so that you do not have to remember which Dockerfile, environment files, and volume mounts belong together – you name a profile, and the script assembles the rest.

The image it builds is also the basis for the other workflows: Running on HPC clusters converts it to an Apptainer image, and Cloud Deployment provisions a machine that runs it.

Caution

The standard Isaac Lab container depends on the Isaac Sim Docker image. By running that container, you are implicitly agreeing to the NVIDIA Software License Agreement. If you do not agree to the EULA, do not run that container. The kitless image contains neither Isaac Sim nor Kit.

Prerequisites#

Install Docker Engine, Docker Compose, and the NVIDIA Container Toolkit. The container is tested with Docker Engine 26.0.0 and Docker Compose 2.25.0; use these versions or newer. Follow the post-installation steps so that Docker runs without sudo.

The Isaac Sim documentation on container installation covers the same prerequisites in more detail, including how to obtain access to the Isaac Sim image.

Note

Due to limitations with snap, please make sure the Isaac Lab directory is placed under the /home directory tree when using docker.

Container profiles#

A profile is a Docker Compose profile: docker-compose.yaml tags each service with one, and Compose starts only the services whose profile is active. Naming a profile therefore selects a matched set – one service, one Dockerfile, one set of environment files, and one image name:

Profile

Compose service

Dockerfile

Environment files

Built on top of

base

isaac-lab-base

Dockerfile.base

.env.base

the Isaac Sim image

ros2

isaac-lab-ros2

Dockerfile.ros2

.env.base + .env.ros2

the base image, built first

kitless

isaac-lab-kitless

Dockerfile.kitless

.env.kitless

nothing – standalone, no Isaac Sim

Every command below takes the profile as its first positional argument and defaults to base. Only one profile applies at a time, and the resulting image and container are both named isaac-lab-<profile>. Pass --suffix to append a name suffix when you want several variants of the same profile side by side. Do not use --suffix with cluster deployments, whose export commands expect the unsuffixed image name.

For what each image actually contains and how to choose between them, see Container images.

Start, run, and retrieve results#

From the repository root on the host, build and start the default base container, then open a shell in it. Append a profile to target a different image:

./docker/container.py start
./docker/container.py enter

Inside the container, run the logging example without a visualizer:

uv run isaaclab -p scripts/tutorials/00_sim/log_time.py --viz none

The script writes simulation time at each step to /workspace/isaaclab/logs/docker_tutorial/log.txt. Stop the script with Ctrl+C and type exit to return to the host shell. Retrieve the results before stopping the container:

./docker/container.py copy
./docker/container.py stop

The log is now at docker/artifacts/logs/docker_tutorial/log.txt on the host. The stop command removes the container and its Compose-managed volumes, including logs and caches; copy out anything you need first. The image remains. To remove it after stopping, run docker image rm isaac-lab-base; the next start rebuilds it. See Docker pruning for other cleanup options.

Code for log_time.py
 1# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
 2# All rights reserved.
 3#
 4# SPDX-License-Identifier: BSD-3-Clause
 5
 6"""
 7This script demonstrates how to generate log outputs while the simulation plays.
 8It accompanies the tutorial on docker usage.
 9
10.. code-block:: bash
11
12    # Usage
13    uv run python scripts/tutorials/00_sim/log_time.py
14
15"""
16
17"""Launch Isaac Sim Simulator first."""
18
19
20import argparse
21import os
22
23from isaaclab.app import AppLauncher
24
25# create argparser
26parser = argparse.ArgumentParser(description="Tutorial on creating logs from within the docker container.")
27# append AppLauncher cli args
28AppLauncher.add_app_launcher_args(parser)
29# parse the arguments
30args_cli = parser.parse_args()
31# launch omniverse app
32app_launcher = AppLauncher(args_cli)
33simulation_app = app_launcher.app
34
35"""Rest everything follows."""
36
37from isaaclab.sim import SimulationCfg, SimulationContext
38
39
40def main():
41    """Main function."""
42    # Specify that the logs must be in logs/docker_tutorial
43    log_dir_path = os.path.join("logs")
44    if not os.path.isdir(log_dir_path):
45        os.mkdir(log_dir_path)
46    # In the container, the absolute path will be
47    # /workspace/isaaclab/logs/docker_tutorial, because
48    # all Python execution is done through the uv-managed workspace
49    # and the calling process' path will be /workspace/isaaclab
50    log_dir_path = os.path.abspath(os.path.join(log_dir_path, "docker_tutorial"))
51    if not os.path.isdir(log_dir_path):
52        os.mkdir(log_dir_path)
53    print(f"[INFO] Logging experiment to directory: {log_dir_path}")
54
55    # Initialize the simulation context
56    sim_cfg = SimulationCfg(dt=0.01)
57    sim = SimulationContext(sim_cfg)
58    # Set main camera
59    sim.set_camera_view([2.5, 2.5, 2.5], [0.0, 0.0, 0.0])
60
61    # Play the simulator
62    sim.reset()
63    # Now we are ready!
64    print("[INFO]: Setup complete...")
65
66    # Prepare to count sim_time
67    sim_dt = sim.get_physics_dt()
68    sim_time = 0.0
69
70    # Open logging file
71    with open(os.path.join(log_dir_path, "log.txt"), "w") as log_file:
72        # Simulate physics
73        while simulation_app.is_running():
74            log_file.write(f"{sim_time}" + "\n")
75            # perform step
76            sim.step()
77            sim_time += sim_dt
78
79
80if __name__ == "__main__":
81    # run the main function
82    main()
83    # close sim app
84    simulation_app.close()

Note

The image copies the repository to /workspace/isaaclab at build time, so edits made after the build are not picked up automatically. To keep the development loop fast, the compose file bind-mounts source, scripts, docs, and tools from the host, so changes to those directories appear inside the container immediately. Everything else requires a rebuild.

container.py command reference#

Run ./docker/container.py --help to list commands and ./docker/container.py <command> --help for that command’s arguments.

Command

Description

build

Build the image without creating a container.

start

Build the image and start the container in the background.

enter

Open a Bash shell in the running container.

config

Print the merged Compose configuration. Use --output-yaml <path> to write it to a file instead.

copy

Copy logs, data, and built documentation to docker/artifacts on the host.

stop

Stop and remove the container and its Compose-managed volumes.

Every command accepts the following arguments:

Argument

Description

profile

Optional profile name; defaults to base. Other profiles are ros2 and kitless.

--files [FILE ...]

Merge additional Compose YAML files after docker-compose.yaml, in the supplied order.

--env-files [FILE ...]

Merge additional environment files after the profile defaults, in the supplied order.

--suffix [SUFFIX]

Append -SUFFIX to the image and container names. Omit it for unsuffixed names.

--info

Print the resolved container interface configuration instead of running the command.

-h, --help

Show help and exit.

Extending the Compose configuration#

--files and --env-files merge extra Compose and environment files into the generated configuration, which is how optional components are layered on without editing the checked-in files. Streaming to XR devices is the worked example – it adds the CloudXR Runtime service alongside base:

./docker/container.py start --files docker-compose.cloudxr-runtime.patch.yaml --env-files .env.cloudxr-runtime

Stop it with the same arguments. The teleoperation setup, firewall rules, and client connection steps are covered in Setting up Isaac Teleop with CloudXR. Use ./docker/container.py config to print the merged result when a combination does not behave as expected.

What persists between runs#

The compose file declares named volumes for Isaac Sim caches, logs, and your own data. They remain available while reusing a container, but container.py stop runs docker compose down --volumes and removes them. Run container.py copy before stop to preserve your results on the host.

container.py copy extracts the three volumes you are most likely to want on the host – logs, data_storage, and docs/_build – into docker/artifacts. For anything else, use docker cp, for example docker cp isaac-lab-base:/workspace/isaaclab/logs ..

If you are upgrading from an Isaac Lab image that ran as root, the existing volumes still hold root-owned files that the current uid/gid 1000 runtime user cannot write. Copy out anything worth keeping, then recreate them from the docker directory:

docker compose --file docker-compose.yaml --profile base --env-file .env.base down --volumes
All named volumes and their container paths

Volume Name

Description

Container Path

isaac-cache-kit

Stores cached Kit resources

/isaac-sim/kit/cache

isaac-data-kit

Stores Kit data

/isaac-sim/kit/data

isaac-cache-ov

Stores cached OV resources

/root/.cache/ov

isaac-cache-pip

Stores cached pip resources

/root/.cache/pip

isaac-cache-gl

Stores cached GLCache resources

/root/.cache/nvidia/GLCache

isaac-cache-compute

Stores cached compute resources

/root/.nv/ComputeCache

isaac-cache-uv

Stores uv downloads for the kit-less profile

/home/isaaclab/.cache/uv

isaac-cache-warp

Stores Warp kernels for the kit-less profile

/home/isaaclab/.cache/warp

isaac-logs

Stores logs generated by Omniverse

/root/.nvidia-omniverse/logs

isaac-carb-logs

Stores logs generated by carb

/isaac-sim/kit/logs/Kit/Isaac-Sim

isaac-data

Stores data generated by Omniverse

/root/.local/share/ov/data

isaac-docs

Stores documents generated by Omniverse

/root/Documents

isaac-lab-docs

Stores documentation of Isaac Lab when built inside the container

/workspace/isaaclab/docs/_build

isaac-lab-logs

Stores logs generated by Isaac Lab workflows when run inside the container

/workspace/isaaclab/logs

isaac-lab-data

Stores whatever data users may want to preserve between container runs

/workspace/isaaclab/data_storage

Run docker volume ls on the host to find the actual names, which include the Compose project prefix, then use docker volume inspect <volume-name>. The kitless profile uses the uv and Warp caches and shares the documentation, logs, and data volumes with the others.

Display forwarding with X11#

X11 forwarding lets GUI applications started inside the container display on the host. The first start asks whether to enable it and records the answer in docker/.container.cfg. To change it later, set X11_FORWARDING_ENABLED to 0 or 1 in that file and run start again – the rebuild is what applies the change.