Skip to content

Commit 797acd8

Browse files
committed
fix(docker): support non-root runtime execution
Signed-off-by: oliver könig <okoenig@nvidia.com>
1 parent 9eeeee5 commit 797acd8

2 files changed

Lines changed: 91 additions & 6 deletions

File tree

‎.dockerignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ __pycache__
44
*.pyd
55
.Python
66
env
7+
.venv
78
pip-log.txt
89
pip-delete-this-directory.txt
910
.tox

‎docker/Dockerfile‎

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
# docker buildx build --build-context nemo-gym=. -f docker/Dockerfile \
1212
# --tag <registry>/nemo-gym:latest --push .
1313
#
14+
# Non-root runtime override (the image still defaults to root):
15+
# docker run --rm --user 65532:65532 \
16+
# --env HOME=/home/nemo-runtime \
17+
# --env XDG_CACHE_HOME=/opt/nemo-gym/cache/xdg \
18+
# <registry>/nemo-gym:latest --help
19+
#
1420

1521
ARG BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:26.03-cuda13.2-devel-ubuntu24.04
1622

@@ -68,9 +74,28 @@ EOF
6874

6975
ARG UV_VERSION=0.11.29
7076
ARG PYTHON_VERSION=3.13.14
71-
ENV PATH="/root/.local/bin:$PATH"
72-
RUN curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | sh && \
73-
uv python install ${PYTHON_VERSION}
77+
ENV UV_PYTHON_INSTALL_DIR=/opt/uv/python
78+
ENV UV_CACHE_DIR=/opt/uv/cache
79+
RUN curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | \
80+
env UV_INSTALL_DIR=/usr/local/bin UV_NO_MODIFY_PATH=1 sh && \
81+
/usr/local/bin/uv python install ${PYTHON_VERSION}
82+
ENV PATH="/usr/local/bin:$PATH"
83+
84+
ARG RUNTIME_UID=65532
85+
ARG RUNTIME_GID=65532
86+
RUN if ! getent group "${RUNTIME_GID}" >/dev/null; then \
87+
groupadd --gid "${RUNTIME_GID}" nemo-runtime; \
88+
fi && \
89+
if ! getent passwd "${RUNTIME_UID}" >/dev/null; then \
90+
useradd --no-log-init --uid "${RUNTIME_UID}" --gid "${RUNTIME_GID}" \
91+
--create-home --home-dir /home/nemo-runtime --shell /bin/bash nemo-runtime; \
92+
fi && \
93+
install -d -o "${RUNTIME_UID}" -g "${RUNTIME_GID}" \
94+
/home/nemo-runtime \
95+
/opt/nemo-gym \
96+
/opt/nemo-gym/cache \
97+
/opt/nemo-gym/results \
98+
/opt/uv/cache
7499

75100
ENV RAY_USAGE_STATS_ENABLED=0
76101

@@ -81,13 +106,16 @@ WORKDIR /opt/nemo-gym
81106

82107
ARG BASE_IMAGE
83108
ARG UV_VERSION
109+
ARG RUNTIME_UID
110+
ARG RUNTIME_GID
84111

85112
ENV UV_PROJECT_ENVIRONMENT=/opt/nemo_gym_venv
86113
ENV UV_LINK_MODE=copy
87114

88115
# Copy only dependency metadata first for layer caching.
89-
COPY --from=nemo-gym pyproject.toml uv.lock ./
90-
COPY --from=nemo-gym nemo_gym/__init__.py nemo_gym/package_info.py ./nemo_gym/
116+
COPY --from=nemo-gym --chown=${RUNTIME_UID}:${RUNTIME_GID} pyproject.toml uv.lock ./
117+
COPY --from=nemo-gym --chown=${RUNTIME_UID}:${RUNTIME_GID} \
118+
nemo_gym/__init__.py nemo_gym/package_info.py ./nemo_gym/
91119

92120
RUN uv venv --seed && \
93121
uv sync --link-mode symlink --locked --extra vllm --no-install-project
@@ -111,11 +139,19 @@ LABEL com.nvidia.build.ref="${NVIDIA_BUILD_REF}"
111139

112140
# Copy full source. Exclude pyproject.toml and uv.lock since they are
113141
# already present from the hermetic stage.
114-
COPY --from=nemo-gym --exclude=pyproject.toml --exclude=uv.lock . /opt/nemo-gym
142+
COPY --from=nemo-gym --chown=${RUNTIME_UID}:${RUNTIME_GID} \
143+
--exclude=pyproject.toml --exclude=uv.lock . /opt/nemo-gym
115144

116145
# Install the nemo-gym project itself along with the vllm extra.
117146
RUN UV_LINK_MODE=symlink uv sync --locked --extra vllm
118147

148+
# Keep runtime uv and XDG caches separate from the root-owned build cache that
149+
# backs the symlinked project environment.
150+
ENV UV_CACHE_DIR=/opt/nemo-gym/cache/uv
151+
RUN install -d -o "${RUNTIME_UID}" -g "${RUNTIME_GID}" \
152+
"${UV_CACHE_DIR}" \
153+
/opt/nemo-gym/cache/xdg
154+
119155
# Optional: pre-warm per-server venvs at build time so they are ready at runtime.
120156
# Pass --build-arg NEMO_GYM_PREFETCH_CONFIGS="path/to/config1.yaml,path/to/config2.yaml"
121157
# Uses 'gym env prefetch' which accepts the same config format as 'gym env start',
@@ -126,5 +162,53 @@ if [[ -n "${NEMO_GYM_PREFETCH_CONFIGS:-}" ]]; then
126162
fi
127163
EOF
128164

165+
# Prefetched caches and venvs are built as root. Own the project tree and the
166+
# project environment's directories so the runtime identity can mutate them.
167+
RUN venv_site_packages="$(/opt/nemo_gym_venv/bin/python -c \
168+
'import site; print(site.getsitepackages()[0])')" && \
169+
touch "${venv_site_packages}/.nonroot-uninstall-probe" && \
170+
chown -R "${RUNTIME_UID}:${RUNTIME_GID}" /opt/nemo-gym && \
171+
find /opt/nemo_gym_venv -type d \
172+
-exec chown "${RUNTIME_UID}:${RUNTIME_GID}" {} +
173+
174+
USER ${RUNTIME_UID}:${RUNTIME_GID}
175+
176+
# Runtime callers select this UID and set HOME/cache variables explicitly; the
177+
# published image remains root by default. Prove that the CLI, uv toolchain,
178+
# project environment, source tree, and runtime paths support that contract.
179+
RUN export HOME=/home/nemo-runtime \
180+
XDG_CACHE_HOME=/opt/nemo-gym/cache/xdg && \
181+
test "$(id -u)" = "${RUNTIME_UID}" && \
182+
test -w "${HOME}" && \
183+
test -w "${XDG_CACHE_HOME}" && \
184+
test -w "${UV_CACHE_DIR}" && \
185+
test -w /opt/nemo-gym/cache && \
186+
test -w /opt/nemo-gym/results && \
187+
test -x /usr/local/bin/uv && \
188+
test -x /usr/local/bin/uvx && \
189+
test -x /opt/nemo_gym_venv/bin/python && \
190+
venv_site_packages="$(python -c 'import site; print(site.getsitepackages()[0])')" && \
191+
rm "${venv_site_packages}/.nonroot-uninstall-probe" && \
192+
touch "${HOME}/.nonroot-write-probe" \
193+
"${XDG_CACHE_HOME}/.nonroot-write-probe" \
194+
"${UV_CACHE_DIR}/.nonroot-write-probe" \
195+
/opt/nemo-gym/.nonroot-write-probe \
196+
/opt/nemo-gym/cache/.nonroot-write-probe \
197+
/opt/nemo-gym/results/.nonroot-write-probe \
198+
"${venv_site_packages}/.nonroot-write-probe" && \
199+
gym --help >/dev/null && \
200+
uv venv /opt/nemo-gym/cache/container-smoke && \
201+
/opt/nemo-gym/cache/container-smoke/bin/python -c \
202+
"import sys; assert sys.version_info[:2] == (3, 13)" && \
203+
rm -r /opt/nemo-gym/cache/container-smoke \
204+
"${HOME}/.nonroot-write-probe" \
205+
"${XDG_CACHE_HOME}/.nonroot-write-probe" \
206+
"${UV_CACHE_DIR}/.nonroot-write-probe" \
207+
/opt/nemo-gym/.nonroot-write-probe \
208+
/opt/nemo-gym/cache/.nonroot-write-probe \
209+
/opt/nemo-gym/results/.nonroot-write-probe \
210+
"${venv_site_packages}/.nonroot-write-probe"
211+
212+
USER root
129213
ENTRYPOINT ["gym"]
130214
CMD ["--help"]

0 commit comments

Comments
 (0)