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 |
|---|---|---|---|---|
|
|
|
|
the Isaac Sim image |
|
|
|
|
the |
|
|
|
|
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 the image without creating a container. |
|
Build the image and start the container in the background. |
|
Open a Bash shell in the running container. |
|
Print the merged Compose configuration. Use |
|
Copy logs, data, and built documentation to |
|
Stop and remove the container and its Compose-managed volumes. |
Every command accepts the following arguments:
Argument |
Description |
|---|---|
|
Optional profile name; defaults to |
|
Merge additional Compose YAML files after |
|
Merge additional environment files after the profile defaults, in the supplied order. |
|
Append |
|
Print the resolved container interface configuration instead of running the command. |
|
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.