Make Isaac Lab Docker images run as non-root#5618
Conversation
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot
Summary
This PR implements Docker security best practices by converting the cuRobo image to run as a non-root user (isaaclab with uid/gid 1000). The change includes proper CI verification and comprehensive regression tests to prevent future regressions.
Design Assessment
Architecture: ✅ Well-structured
The implementation follows the correct Docker pattern: perform all root-required setup first, create the non-root user, adjust ownership, then switch to USER isaaclab at the end. The use of --non-unique for uid/gid handles potential conflicts with base image users gracefully.
CI Integration: ✅ Solid
The new verify-curobo-non-root job provides defense-in-depth with both static Dockerfile analysis (pytest) and runtime identity verification.
Findings
🟡 Minor Suggestions
-
docker/Dockerfile.curobo— Hardcoded uid/gid 1000
The comment explains this matches GitHub runner bind mounts, which is appropriate for CI. However, users running locally may have different host uids. Consider documenting this in the container usage docs or providing a--useroverride example. -
.github/workflows/build.yaml:262-271— First run step lacks explicit error handling
While pytest exit codes propagate correctly, addingset -euo pipefailat the start of the shell block would match the defensive style used in the second step.
✅ Good Patterns Observed
--non-uniqueflag handles edge case where uid/gid 1000 already exists in base imagechmod 755 ${ISAACSIM_ROOT_PATH}opens traversal without recursively changing inner file permissionsDOCKERFILE_RUNTIME_USERSmapping forces explicit decisions for new Dockerfiles (regression protection)- Runtime verification (
id -u != 0) as separate CI step provides independent validation
Test Coverage
Coverage: ✅ Good
- Static analysis tests verify Dockerfile structure
- Runtime CI job verifies actual container behavior
test_all_dockerfiles_have_runtime_user_expectations()prevents new Dockerfiles from escaping review
Minor gap: No direct test that the non-root user can actually execute Isaac Lab scripts (though the pytest run itself serves as an indirect functional test).
CI Status
⚠️ pre-commit: failed (may be unrelated to this PR — please verify)- 🔄 Docker builds: pending
- ✅ Changelog, links, labeler: passed
Verdict
Minor fixes needed — The implementation is sound and follows security best practices. Address the pre-commit failure and consider the minor suggestions above. Once CI passes, this is ready to ship. 🚀
Update (e9f0135): Reviewed incremental changes. The implementation is complete and well-structured. The .bashrc path fix (using ${DOCKER_USER_HOME} instead of hardcoded /root) is a good catch. Previous minor suggestions (uid/gid docs, set -euo pipefail) are non-blocking. No new issues found. ✅
Update (b772981): Both minor suggestions from my initial review have been addressed:
- ✅ Added documentation in
docs/source/deployment/docker.rstexplaining uid/gid 1000 and the--useroverride option - ✅ Added
set -euo pipefailto the CI shell blocks
No new issues found. LGTM! 🚀
Update (81b2b4e): PR scope expanded — base and ROS2 images now also run as non-root user. Changes:
- ✅
Dockerfile.base— Createsisaaclabuser (uid/gid 1000), sets correct ownership, switches to non-root at end - ✅
Dockerfile.ros2— Properly handles non-root base:USER rootfor apt setup, restoresUSER isaaclabat end - ✅
docker-compose.yaml— RemovedOMNI_KIT_ALLOW_ROOT=1(no longer needed) - ✅ New
verify-base-non-rootCI job mirrors the cuRobo verification pattern - ✅ Test coverage updated:
DOCKERFILE_RUNTIME_USERSmap reflects new expectations, newtest_ros2_dockerfile_restores_non_root_runtime_user()test added - ✅ Documentation updated consistently
Implementation is consistent with the previously approved cuRobo pattern. No new issues found. LGTM! 🚀
Update (c9b944a): CI test action updated to handle volume-mounted containers correctly:
- ✅ When bind-mounting source code, container now runs as host uid/gid with
--user ${host_uid}:${host_gid} - ✅ Proper environment variables set (
HOME,XDG_CACHE_HOME,USER,LOGNAME) for the mounted user - This ensures correct file ownership and prevents permission issues with the mounted volume
Good fix for CI compatibility with the non-root images. No issues found. ✅
Update (d1acb0f): Enhanced CI runtime storage for volume-mounted containers:
- ✅ Creates dedicated
docker_runtime_dirwith full Kit/Omniverse directory hierarchy - ✅ Mounts writable volumes for cache, config, data, and logs (mirrors compose/singularity setup)
- ✅ Adds
XDG_DATA_HOMEenvironment variable for proper XDG compliance - ✅ Cleanup logic in both trap handler and normal exit path
This addresses Kit writing generated files outside the source tree — provides proper writable storage for the non-root user in CI. Well done! ✅
e9f0135 to
b772981
Compare
|
Addressed the review-bot suggestions in b772981:
Pre-commit is passing; Docker/docs/license checks are still running. |
Greptile SummaryThis PR migrates the Isaac Lab
Confidence Score: 3/5Safe to merge on a clean install; breaks existing deployments with root-owned named volumes until they are manually pruned. The code changes themselves are structurally correct and the new non-root images work as advertised on a fresh setup. The gap is operational: users who already have running deployments with persistent Docker named volumes will get permission errors on first launch because those volumes retain root ownership. The upgrade path is undocumented in the added docker.rst note. docs/source/deployment/docker.rst needs a migration note for existing named volumes; docker/Dockerfile.base and docker/Dockerfile.curobo have the chmod 755 /root concern.
|
| Filename | Overview |
|---|---|
| docker/Dockerfile.base | Adds non-root runtime user isaaclab at uid/gid 1000 with home=/root; applies chmod 755 to /root (weakening its standard 700 perms) and chown -R to transfer ownership |
| docker/Dockerfile.curobo | Mirror of Dockerfile.base non-root changes; adds isaaclab user, chown, chmod 755 on /root, and USER isaaclab at end |
| docker/Dockerfile.ros2 | Adds USER root at start and USER isaaclab at end to bracket the ROS 2 apt installation; uses --chown=isaaclab:isaaclab for COPY |
| docker/docker-compose.yaml | Removes OMNI_KIT_ALLOW_ROOT=1 from shared environment; existing named Docker volumes retain root ownership and will be unwritable by uid 1000 on upgrade |
| .github/actions/run-tests/action.yml | Adds writable bind-mount tree under RUNNER_TEMP for volume-mount mode, runs container with host uid:gid, and overrides HOME/XDG env vars to avoid writes to /root |
| .github/workflows/build.yaml | Adds verify-base-non-root and verify-curobo-non-root CI jobs that pull images and assert runtime uid != 0 |
| docker/test/test_dockerfile_nonroot.py | New static regression test: verifies final USER directive, user creation commands, and ROS 2 root→isaaclab sequence; enforces explicit runtime-user declaration for any new Dockerfiles |
| docs/source/deployment/docker.rst | Adds note about uid/gid 1000 and --user flag; missing guidance on pruning existing root-owned named volumes before upgrading |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[FROM isaacsim base] --> B[USER root]
B --> C[apt-get install deps]
C --> D[COPY source files]
D --> E[pip install Isaac Lab deps]
E --> F[Write .bashrc aliases to DOCKER_USER_HOME]
F --> G[groupadd/useradd isaaclab uid=1000 gid=1000 home=/root]
G --> H[chown -R isaaclab:isaaclab ISAACLAB_PATH + DOCKER_USER_HOME]
H --> I[chmod 755 ISAACSIM_ROOT_PATH + DOCKER_USER_HOME]
I --> J[COPY --chown=isaaclab:isaaclab ../ to ISAACLAB_PATH]
J --> K[USER isaaclab]
K --> L[WORKDIR ISAACLAB_PATH]
subgraph ROS2 Overlay
M[FROM isaac-lab-base] --> N[USER root]
N --> O[apt-get install ROS 2 Humble]
O --> P[COPY --chown=isaaclab:isaaclab .ros/]
P --> Q[USER isaaclab]
end
subgraph cuRobo Overlay
R[FROM isaacsim base] --> S[same root setup as base]
S --> T[pip install cuRobo extras]
T --> U[groupadd/useradd isaaclab uid=1000]
U --> V[USER isaaclab]
end
Reviews (1): Last reviewed commit: "Fix Docker test runtime mounts for non-r..." | Re-trigger Greptile
| The Isaac Lab base, ROS 2, and cuRobo images run as a non-root user with uid/gid 1000 to keep | ||
| bind-mounted workspaces writable on GitHub runners. If you run one of these images directly with | ||
| ``docker run`` and your host uid/gid differs, pass Docker's ``--user "$(id -u):$(id -g)"`` option so | ||
| new files on bind mounts are owned by your host user. |
There was a problem hiding this comment.
Upgrade path breaks existing named volumes
The new note covers the --user flag for docker run, but omits a more impactful case: users upgrading from the root-based images with existing Docker named volumes (isaac-cache-kit, isaac-cache-ov, isaac-cache-compute, etc.). Named volumes persist their data across image rebuilds and are not re-initialised when the image changes. Old volumes contain root-owned files; the isaaclab user at uid 1000 cannot write to them, so Isaac Sim will fail on first launch with permission errors until the volumes are recreated. The note should direct existing users to prune the affected volumes before running the new image (e.g., docker volume rm isaac-cache-ov isaac-cache-pip isaac-cache-compute … or docker volume prune --filter label=com.docker.compose.project=…).
There was a problem hiding this comment.
✅ Resolved — Documentation now includes upgrade instructions with docker compose ... down --volumes for users migrating from root-based images.
| # Open up traversal of the Isaac Sim root and runtime home for non-root users. | ||
| # Inner Isaac Sim files keep their original permissions, so avoid chowning the | ||
| # full install. | ||
| RUN chmod 755 \ | ||
| ${ISAACSIM_ROOT_PATH} \ | ||
| ${DOCKER_USER_HOME} |
There was a problem hiding this comment.
chmod 755 on ${DOCKER_USER_HOME} (which resolves to /root) relaxes the standard 700 permissions on the root home directory. With 755, any process running inside the container as a different uid can traverse into /root and read its contents. In a single-user container this is low-risk, but it is a deliberate security relaxation worth making explicit. Consider 750 to allow group traversal (isaaclab group) while blocking world access, or document why 755 is required.
| # Open up traversal of the Isaac Sim root and runtime home for non-root users. | |
| # Inner Isaac Sim files keep their original permissions, so avoid chowning the | |
| # full install. | |
| RUN chmod 755 \ | |
| ${ISAACSIM_ROOT_PATH} \ | |
| ${DOCKER_USER_HOME} | |
| # Open up traversal of the Isaac Sim root and runtime home for non-root users. | |
| # Inner Isaac Sim files keep their original permissions, so avoid chowning the | |
| # full install. | |
| # 750: owner (isaaclab) rwx, group r-x, world --- | |
| RUN chmod 755 \ | |
| ${ISAACSIM_ROOT_PATH} \ | |
| && chmod 750 \ | |
| ${DOCKER_USER_HOME} |
There was a problem hiding this comment.
✅ Resolved — Changed to chmod 750 for ${DOCKER_USER_HOME}, restricting world access while allowing group traversal.
| # Open up traversal of the Isaac Sim root and runtime home for non-root users. | ||
| # Inner Isaac Sim files keep their original permissions, so avoid chowning the | ||
| # full install. | ||
| RUN chmod 755 \ | ||
| ${ISAACSIM_ROOT_PATH} \ | ||
| ${DOCKER_USER_HOME} |
There was a problem hiding this comment.
Same
chmod 755 on ${DOCKER_USER_HOME} (/root) as in Dockerfile.base — makes /root world-traversable. Consider 750 for consistency with any hardening applied to the base Dockerfile.
| # Open up traversal of the Isaac Sim root and runtime home for non-root users. | |
| # Inner Isaac Sim files keep their original permissions, so avoid chowning the | |
| # full install. | |
| RUN chmod 755 \ | |
| ${ISAACSIM_ROOT_PATH} \ | |
| ${DOCKER_USER_HOME} | |
| # Open up traversal of the Isaac Sim root and runtime home for non-root users. | |
| # Inner Isaac Sim files keep their original permissions, so avoid chowning the | |
| # full install. | |
| RUN chmod 755 \ | |
| ${ISAACSIM_ROOT_PATH} \ | |
| && chmod 750 \ | |
| ${DOCKER_USER_HOME} |
There was a problem hiding this comment.
✅ Resolved — Changed to chmod 750 for ${DOCKER_USER_HOME}, consistent with the hardening applied to Dockerfile.base.
Signed-off-by: sheikh-nv <7774242+sheikh-nv@users.noreply.github.com>
Extend the non-root runtime user setup to the base image, restore the runtime user after ROS2 apt setup, and update docs, compose defaults, and CI coverage for the expanded scope.
Mount writable runtime storage for volume-mounted Docker CI jobs so Kit cache, config, data, and log files do not write into the Isaac Sim install tree while tests run as the host uid/gid.
d1acb0f to
88400eb
Compare
|
Update (88400eb): All previous inline review comments have been addressed:
No new issues found. LGTM 👍 |
|
🤖 Incremental Review Update (c34c247) Reviewed the new commits since my last review (88400eb → c34c247). Changes include:
✅ No new issues in the incremental changes. ⏳ Previous concerns remain open (original inline comments still visible):
|
🤖 Incremental Review: f9e8322Commit: Adjust CI Kit startup threshold Changes:
Assessment: ✅ Looks GoodThis is a sensible adjustment. The non-root Docker images use isolated writable runtime/cache mounts in CI, which results in colder startup than reused local caches. The 5-second buffer (15s→20s) accounts for this appropriately. Code quality:
No new issues found. Previous inline review comments still apply. 🚀 |
# Description Makes the Isaac Lab base, ROS 2, and cuRobo Docker images run as a non-root runtime user by creating an `isaaclab` user after root-only setup and switching the final images to `USER isaaclab`. The ROS 2 Dockerfile temporarily switches back to `USER root` for apt-based ROS setup, then restores `USER isaaclab` for runtime. The `installci` Dockerfile is intentionally unchanged. This also updates deprecated Isaac Sim Dockerfile comments to point to the NGC Isaac Sim container page, removes the default root-allowance compose setting, updates Docker documentation, and adds CI coverage to verify the built base and cuRobo images do not run as root by default. ROS 2 is covered by the static Dockerfile regression test because this workflow does not build a ROS 2 image. Fixes: N/A Validation: - `./isaaclab.sh -f` - `git diff --check` - `docker run ... /isaac-sim/python.sh -m pytest docker/test/test_dockerfile_nonroot.py -q` -> `7 passed, 1 skipped` - Manual cuRobo runtime check confirmed non-root `uid=1000` ## Type of change - Bug fix (non-breaking change which fixes an issue) ## Screenshots N/A ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package (not applicable; no `source/<pkg>/` package touched) - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Signed-off-by: sheikh-nv <7774242+sheikh-nv@users.noreply.github.com> Co-authored-by: Kelly Guo <kellyg@nvidia.com>
# Description Since the non-root Docker migration (#5618, first shipped in 3.0.0-beta2), the container runs as user isaaclab (uid/gid 1000) with HOME=/root. Persistent mounts at /root/.local/share/ov/data that were created by an older root-based image (stale named volumes) or by Docker as auto-created bind-mount dirs are owned by root, so the runtime user cannot write the extension-registry cache. For XR teleop this aborts startup with a confusing, seemingly-unrelated error: PermissionError: [Errno 13] Permission denied: '/root/.local/share/ov/data/exts' ... No versions of omni.kit.xr.bundle.generic that satisfies: isaaclab.python.xr.openxr-3.0.0 ... Exiting app because of dependency solver failure... The XR bundle isn't actually missing — the registry never synced because its cache dir couldn't be created. This PR documents the cause and fix: docs/source/how-to/cloudxr_teleoperation.rst: adds an admonition to the "Run with Docker" section explaining the failure and the fix (recreate/chown volumes for Compose, pre-create/chown host dirs for single-container). docs/source/deployment/docker.rst: warns that non-root prebuilt images need bind-mount host dirs pre-created and chowned to uid/gid 1000, with a copy-paste snippet. Docs-only; no code changes and no changelog fragment (no source/<pkg>/ package touched). Fixes # (issue) ## Type of change - Documentation update ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
# Description Since the non-root Docker migration (#5618, first shipped in 3.0.0-beta2), the container runs as user `isaaclab` (uid/gid 1000) with `HOME=/root`. Persistent mounts at `/root/.local/share/ov/data` that were created by an older root-based image (stale named volumes) or by Docker as auto-created bind-mount dirs are owned by `root`, so the runtime user cannot write the extension-registry cache. For XR teleop this aborts startup with a confusing, seemingly-unrelated error: PermissionError: [Errno 13] Permission denied: '/root/.local/share/ov/data/exts' ... No versions of omni.kit.xr.bundle.generic that satisfies: isaaclab.python.xr.openxr-3.0.0 ... Exiting app because of dependency solver failure... The XR bundle isn't actually missing — the registry never synced because its cache dir couldn't be created. This PR documents the cause and fix: - **`docs/source/how-to/cloudxr_teleoperation.rst`**: adds an admonition to the "Run with Docker" section explaining the failure and the fix (recreate/chown volumes for Compose, pre-create/chown host dirs for single-container). - **`docs/source/deployment/docker.rst`**: warns that non-root prebuilt images need bind-mount host dirs pre-created and chowned to uid/gid 1000, with a copy-paste snippet. Docs-only; no code changes and no changelog fragment (no `source/<pkg>/` package touched). Fixes # (issue) ## Type of change - Documentation update ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
…clab user (#6082) ## Summary - Fixes training regressions in the non-root Docker images (base + cuRobo): `skrl` training aborts with `PermissionError` creating `logs/`, and Isaac Sim emits `omni.datastore` lock failures under `kit/cache` (tripping the CI training-log blacklist). - Root cause: a fresh Docker named volume inherits ownership from the image directory at its mount path; the non-root `isaaclab` user (uid/gid 1000) cannot write mount points the image left missing or root-owned. - Pre-creates and `chown`s every named-volume mount point before the `USER` switch, driven by a single source of truth — `docker-compose.yaml`, parsed by `docker/utils/volume_mounts.py` — so the list is never duplicated in the Dockerfiles. ## 1. Background #5618 switched the base / ROS 2 / cuRobo images to `USER isaaclab` (uid/gid 1000) so bind-mounted workspaces stay writable for the host/runner user. That change covered the `${DOCKER_USER_HOME}` mount points (swept by the recursive `chown`), but not the named volumes that mount outside the runtime home. ## 2. Mechanism When an empty named volume is mounted for the first time, Docker (the root daemon) seeds the volume's ownership from the image directory at that path. If that directory is missing or root-owned in the image, the volume comes up `root:root` and the uid-1000 runtime user is denied. The home-based volumes already inherited the right owner; the ones under `/isaac-sim` and `/workspace/isaaclab` did not (`kit/cache` was created root-owned by a build `mkdir`; `logs` / `data_storage` / `docs/_build` did not exist in the image at all). ## 3. Fix 3.1 `docker/utils/volume_mounts.py` — parses the `type: volume` targets out of `docker-compose.yaml` (the single source of truth) and resolves the `${VAR}` paths. Reuses PyYAML, already an IsaacLab dependency. 3.2 `Dockerfile.base` / `Dockerfile.curobo` — before `USER isaaclab`, invoke the parser and `mkdir -p` + `chown -R isaaclab:isaaclab` every mount point, so fresh volumes inherit `isaaclab` ownership. `set -o pipefail` plus a non-empty guard abort the build if the parse yields nothing, rather than silently shipping an unprepared mount point. The ROS 2 image builds `FROM` base and inherits the fix. 3.3 Adding a volume to `docker-compose.yaml` is now prepared automatically — no second list to keep in sync. ## 4. Test `docker/test/test_dockerfile_nonroot.py` unit-tests the parser (asserting it returns the mount points that triggered the regression) and asserts each non-root Dockerfile wires the parser in with the guard. Static, no image build required. ## 5. Validation - Static tests pass (`13 passed`); the parser test fails if compose drops a tracked volume. - Verified on a freshly built base image: all named-volume mount points come up `uid 1000`, and the exact QA training (`skrl` Anymal-C) runs to completion with no `PermissionError` and zero `omni.datastore` lock failures. ## 6. Notes - No changelog fragment: `docker/` is not under `source/<pkg>` (matches #5618). - cuRobo has no compose file today, but it creates `kit/cache` as root and runs Isaac Sim non-root, so its cache is unwritable even without a volume mount; the same step fixes it.
…clab user (cherry-pick #6082 → 3.0.0-beta2) (#6095) ## Summary - Cherry-pick of #6082 onto `release/3.0.0-beta2`. Net diff is identical to the develop-side PR; applies cleanly with no conflicts. - Fixes training regressions in the non-root Docker images (base + cuRobo): `skrl` training aborts with `PermissionError` creating `logs/`, and Isaac Sim emits `omni.datastore` lock failures under `kit/cache` (tripping the CI training-log blacklist). - Root cause: a fresh Docker named volume inherits ownership from the image directory at its mount path; the non-root `isaaclab` user (uid/gid 1000) cannot write mount points the image left missing or root-owned. - Pre-creates and `chown`s every named-volume mount point before the `USER` switch, driven by a single source of truth — `docker-compose.yaml`, parsed by `docker/utils/volume_mounts.py` — so the list is never duplicated in the Dockerfiles. ## 1. Background #5618 switched the base / ROS 2 / cuRobo images to `USER isaaclab` (uid/gid 1000) so bind-mounted workspaces stay writable for the host/runner user. That change covered the `${DOCKER_USER_HOME}` mount points (swept by the recursive `chown`), but not the named volumes that mount outside the runtime home. ## 2. Mechanism When an empty named volume is mounted for the first time, Docker (the root daemon) seeds the volume's ownership from the image directory at that path. If that directory is missing or root-owned in the image, the volume comes up `root:root` and the uid-1000 runtime user is denied. The home-based volumes already inherited the right owner; the ones under `/isaac-sim` and `/workspace/isaaclab` did not (`kit/cache` was created root-owned by a build `mkdir`; `logs` / `data_storage` / `docs/_build` did not exist in the image at all). ## 3. Fix 3.1 `docker/utils/volume_mounts.py` — parses the `type: volume` targets out of `docker-compose.yaml` (the single source of truth) and resolves the `${VAR}` paths. Reuses PyYAML, already an IsaacLab dependency. 3.2 `Dockerfile.base` / `Dockerfile.curobo` — before `USER isaaclab`, invoke the parser and `mkdir -p` + `chown -R isaaclab:isaaclab` every mount point, so fresh volumes inherit `isaaclab` ownership. `set -o pipefail` plus a non-empty guard abort the build if the parse yields nothing, rather than silently shipping an unprepared mount point. The ROS 2 image builds `FROM` base and inherits the fix. 3.3 Adding a volume to `docker-compose.yaml` is now prepared automatically — no second list to keep in sync. ## 4. Test `docker/test/test_dockerfile_nonroot.py` unit-tests the parser (asserting it returns the mount points that triggered the regression) and asserts each non-root Dockerfile wires the parser in with the guard. Static, no image build required. ## 5. Validation - Static tests pass (`13 passed`); the parser test fails if compose drops a tracked volume. - Verified on a freshly built base image: all named-volume mount points come up `uid 1000`, and the exact QA training (`skrl` Anymal-C) runs to completion with no `PermissionError` and zero `omni.datastore` lock failures. ## 6. Notes - No changelog fragment: `docker/` is not under `source/<pkg>` (matches #5618). - cuRobo has no compose file today, but it creates `kit/cache` as root and runs Isaac Sim non-root, so its cache is unwritable even without a volume mount; the same step fixes it.
Description
Makes the Isaac Lab base, ROS 2, and cuRobo Docker images run as a non-root runtime user by creating an
isaaclabuser after root-only setup and switching the final images toUSER isaaclab.The ROS 2 Dockerfile temporarily switches back to
USER rootfor apt-based ROS setup, then restoresUSER isaaclabfor runtime. TheinstallciDockerfile is intentionally unchanged.This also updates deprecated Isaac Sim Dockerfile comments to point to the NGC Isaac Sim container page, removes the default root-allowance compose setting, updates Docker documentation, and adds CI coverage to verify the built base and cuRobo images do not run as root by default. ROS 2 is covered by the static Dockerfile regression test because this workflow does not build a ROS 2 image.
Fixes: N/A
Validation:
./isaaclab.sh -fgit diff --checkdocker run ... /isaac-sim/python.sh -m pytest docker/test/test_dockerfile_nonroot.py -q->7 passed, 1 skippeduid=1000Type of change
Screenshots
N/A
Checklist
pre-commitchecks with./isaaclab.sh --formatsource/<pkg>/changelog.d/for every touched package (not applicable; nosource/<pkg>/package touched)CONTRIBUTORS.mdor my name already exists there