> This page is for version Release 3.0 (default).
> For other versions, use one of these documentation indexes:
> - Release 3.0 (default): https://docs.nvidia.com/isaaclab/3.0/llms.txt
> - Release 2.3: https://docs.nvidia.com/isaaclab/2.3/llms.txt
> - Release 2.2: https://docs.nvidia.com/isaaclab/2.2/llms.txt
> - Release 2.1: https://docs.nvidia.com/isaaclab/2.1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/isaaclab/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/isaaclab/_mcp/server.

# 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](/source/workflows/docker/cluster) converts it
to an Apptainer image, and [Cloud Deployment](/source/workflows/docker/cloud) provisions a machine that runs it.

> **Warning**
>
> 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](https://www.nvidia.com/en-us/agreements/enterprise-software/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](https://docs.docker.com/engine/install/), [Docker Compose](https://docs.docker.com/compose/install/), and the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html).
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](https://docs.docker.com/engine/install/linux-postinstall/) so that Docker runs without `sudo`.

The Isaac Sim documentation on [container installation](https://docs.isaacsim.omniverse.nvidia.com/latest/installation/install_container.html) covers the same prerequisites in more
detail, including how to obtain access to the Isaac Sim image.

> **Note**
>
> Due to limitations with [snap](https://snapcraft.io/docs/home-outside-home), 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](https://docs.docker.com/compose/how-tos/profiles/):
`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](/source/workflows/docker/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:

```bash
./docker/container.py start
./docker/container.py enter
```

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

```bash
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:

```bash
./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](https://docs.docker.com/engine/manage-resources/pruning/) for other cleanup options.

```python
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""
This script demonstrates how to generate log outputs while the simulation plays.
It accompanies the tutorial on docker usage.

~~~bash
# Usage
uv run python scripts/tutorials/00_sim/log_time.py
~~~

"""

"""Parse the command-line arguments first."""

import argparse
import os

from isaaclab.app import add_launcher_args, launch_simulation

# create argparser
parser = argparse.ArgumentParser(description="Tutorial on creating logs from within the docker container.")
# append simulation launcher cli args
add_launcher_args(parser)
# parse the arguments
args_cli = parser.parse_args()

"""Rest everything follows."""

from isaaclab.sim import SimulationCfg, SimulationContext

def main():
    """Main function."""
    # Specify that the logs must be in logs/docker_tutorial
    log_dir_path = os.path.join("logs")
    if not os.path.isdir(log_dir_path):
        os.mkdir(log_dir_path)
    # In the container, the absolute path will be
    # /workspace/isaaclab/logs/docker_tutorial, because
    # all Python execution is done through the uv-managed workspace
    # and the calling process' path will be /workspace/isaaclab
    log_dir_path = os.path.abspath(os.path.join(log_dir_path, "docker_tutorial"))
    if not os.path.isdir(log_dir_path):
        os.mkdir(log_dir_path)
    print(f"[INFO] Logging experiment to directory: {log_dir_path}")

    # Configure the simulation
    sim_cfg = SimulationCfg(dt=0.01, device=args_cli.device)
    # Launch the simulator runtime that the configuration needs
    with launch_simulation(sim_cfg, args_cli):
        # Initialize the simulation context
        sim = SimulationContext(sim_cfg)
        # Set main camera
        sim.set_camera_view([2.5, 2.5, 2.5], [0.0, 0.0, 0.0])

        # Play the simulator
        sim.reset()
        # Now we are ready!
        print("[INFO]: Setup complete...")

        # Prepare to count sim_time
        sim_dt = sim.get_physics_dt()
        sim_time = 0.0

        # Open logging file
        with open(os.path.join(log_dir_path, "log.txt"), "w") as log_file:
            # Simulate physics
            while sim.is_running():
                log_file.write(f"{sim_time}" + "\n")
                # perform step
                sim.step()
                sim_time += sim_dt

if __name__ == "__main__":
    # run the main function
    main()
```

> **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

## `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`:

```bash
./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 Capture with CloudXR](/source/how-to/cloudxr_teleoperation). 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:

```bash
docker compose --file docker-compose.yaml --profile base --env-file .env.base down --volumes
```

| 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.