11# syntax=docker/dockerfile:1.10.0
22# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33# SPDX-License-Identifier: Apache-2.0
4+ #
5+ # Throughout this file, we make certain paths group-writable because this allows
6+ # both the dynamo user (UID 1000) and Dev Container users (UID != 1000) to work
7+ # properly without needing slow chown -R operations (which can add 2-10 extra
8+ # minutes).
9+ #
10+ # DEVELOPMENT PATHS THAT MUST BE GROUP-WRITABLE (for non-virtualenv containers):
11+ # /workspace - Users create/modify project files
12+ # /home/dynamo - Users create config/cache files
13+ # /home/dynamo/.local - SGLang uses $HOME/.local/lib/python3.10/site-packages for pip install
14+ #
15+ # HOW TO ACHIEVE GROUP-WRITABLE PERMISSIONS:
16+ # 1. SHELL + /etc/profile.d - Login shell sources umask 002 globally for all RUN commands (775/664)
17+ # 2. COPY --chmod=775 - Sets permissions on copied children (not destination)
18+ # 3. chmod g+w (no -R) - Fixes destination dirs only (milliseconds vs minutes)
419
520# This section contains build arguments that are common and shared with
621# the plain Dockerfile, so they should NOT have a default. The source of truth is from build.sh.
@@ -190,18 +205,25 @@ RUN git clone --depth 1 --branch ${GDRCOPY_COMMIT} https://github.com/NVIDIA/gdr
190205# Fix DeepEP IBGDA symlink
191206RUN ln -sf /usr/lib/$(uname -m)-linux-gnu/libmlx5.so.1 /usr/lib/$(uname -m)-linux-gnu/libmlx5.so
192207
193- # Create dynamo user EARLY - before copying files, with group 0 for OpenShift compatibility
208+ # Create dynamo user with group 0 for OpenShift compatibility
194209RUN userdel -r ubuntu > /dev/null 2>&1 || true \
195210 && useradd -m -s /bin/bash -g 0 dynamo \
196211 && [ `id -u dynamo` -eq 1000 ] \
197212 && mkdir -p /workspace /home/dynamo/.cache /opt/dynamo \
198- && chown -R dynamo: /sgl-workspace /workspace /home/dynamo /opt/dynamo \
199- && chmod -R g+w /sgl-workspace /workspace /home/dynamo/.cache /opt/dynamo
213+ # Non-recursive chown - only the directories themselves, not contents
214+ && chown dynamo:0 /sgl-workspace /workspace /home/dynamo /home/dynamo/.cache /opt/dynamo \
215+ # No chmod needed: umask 002 handles new files, COPY --chmod handles copied content
216+ # Set umask globally for all subsequent RUN commands (must be done as root before USER dynamo)
217+ # NOTE: Setting ENV UMASK=002 does NOT work - umask is a shell builtin, not an environment variable
218+ && mkdir -p /etc/profile.d && echo 'umask 002' > /etc/profile.d/00-umask.sh
200219
201220USER dynamo
202221ENV HOME=/home/dynamo
222+ # This picks up the umask 002 from the /etc/profile.d/00-umask.sh file for subsequent RUN commands
223+ SHELL ["/bin/bash", "-l", "-o", "pipefail", "-c"]
203224
204- # Install SGLang (requires CUDA 12.8.1 or 12.9.1)
225+ # Install SGLang (requires CUDA 12.8.1 or 12.9.1). Note that when system-wide packages is not writable,
226+ # so it gets installed to ~/.local/lib/python<version>/site-packages.
205227RUN python3 -m pip install --no-cache-dir --ignore-installed pip==25.3 setuptools==80.9.0 wheel==0.45.1 html5lib==1.1 six==1.17.0 \
206228 && git clone --depth 1 --branch v${SGLANG_COMMIT} https://github.com/sgl-project/sglang.git \
207229 && cd sglang \
@@ -279,8 +301,10 @@ RUN --mount=type=secret,id=aws-key-id,env=AWS_ACCESS_KEY_ID \
279301 NVSHMEM_DIR=${NVSHMEM_DIR} TORCH_CUDA_ARCH_LIST="9.0;10.0" pip install --no-build-isolation .
280302
281303# Copy rust installation from dynamo_base to avoid duplication efforts
282- COPY --from=dynamo_base /usr/local/rustup /usr/local/rustup
283- COPY --from=dynamo_base /usr/local/cargo /usr/local/cargo
304+ # Pattern: COPY --chmod=775 <path>; RUN chmod g+w <path> because COPY --chmod only affects <path>/*, not <path>
305+ COPY --from=dynamo_base --chown=dynamo:0 --chmod=775 /usr/local/rustup /usr/local/rustup
306+ COPY --from=dynamo_base --chown=dynamo:0 --chmod=775 /usr/local/cargo /usr/local/cargo
307+ RUN chmod g+w /usr/local/rustup /usr/local/cargo
284308
285309ENV RUSTUP_HOME=/usr/local/rustup \
286310 CARGO_HOME=/usr/local/cargo \
@@ -341,8 +365,9 @@ COPY --from=dynamo_base $NIXL_PREFIX $NIXL_PREFIX
341365ENV PATH=/usr/local/bin/etcd/:/usr/local/cuda/nvvm/bin:${HOME}/.local/bin:$PATH
342366
343367# Install Dynamo wheels from dynamo_base wheelhouse
344- COPY --chown=dynamo: benchmarks/ /opt/dynamo/benchmarks/
345- COPY --chown=dynamo: --from=dynamo_base /opt/dynamo/wheelhouse/ /opt/dynamo/wheelhouse/
368+ # Pattern: COPY --chmod=775 <path>; chmod g+w <path> done later as root because COPY --chmod only affects <path>/*, not <path>
369+ COPY --chmod=775 --chown=dynamo:0 benchmarks/ /opt/dynamo/benchmarks/
370+ COPY --chmod=775 --chown=dynamo:0 --from=dynamo_base /opt/dynamo/wheelhouse/ /opt/dynamo/wheelhouse/
346371RUN python3 -m pip install \
347372 /opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl \
348373 /opt/dynamo/wheelhouse/ai_dynamo*any.whl \
@@ -361,29 +386,34 @@ RUN --mount=type=bind,source=./container/deps/requirements.txt,target=/tmp/requi
361386 --requirement /tmp/requirements.test.txt
362387
363388## Copy attribution files and launch banner with correct ownership
364- COPY --chown=dynamo: ATTRIBUTION* LICENSE /workspace/
389+ COPY --chmod=664 --chown=dynamo:0 ATTRIBUTION* LICENSE /workspace/
390+
391+ # Copy tests, benchmarks, deploy and components for CI with correct ownership
392+ # Pattern: COPY --chmod=775 <path>; chmod g+w <path> done later as root because COPY --chmod only affects <path>/*, not <path>
393+ COPY --chmod=775 --chown=dynamo:0 pyproject.toml /workspace/
394+ COPY --chmod=775 --chown=dynamo:0 tests /workspace/tests
395+ COPY --chmod=775 --chown=dynamo:0 examples /workspace/examples
396+ COPY --chmod=775 --chown=dynamo:0 benchmarks /workspace/benchmarks
397+ COPY --chmod=775 --chown=dynamo:0 deploy /workspace/deploy
398+ COPY --chmod=775 --chown=dynamo:0 components/ /workspace/components/
399+ COPY --chmod=775 --chown=dynamo:0 recipes/ /workspace/recipes/
365400
366401# Setup launch banner in common directory accessible to all users
367402RUN --mount=type=bind,source=./container/launch_message/runtime.txt,target=/opt/dynamo/launch_message.txt \
368403 sed '/^#\s/d' /opt/dynamo/launch_message.txt > /opt/dynamo/.launch_screen
369404
370405# Setup environment for all users
371406USER root
372- RUN chmod 755 /opt/dynamo/.launch_screen && \
407+ # Fix directory permissions: COPY --chmod only affects contents, not the directory itself
408+ RUN chmod g+w /workspace /workspace/* /opt/dynamo /opt/dynamo/* && \
409+ chown dynamo:0 /workspace /opt/dynamo/ && \
410+ chmod 755 /opt/dynamo/.launch_screen && \
373411 echo 'source /opt/dynamo/venv/bin/activate' >> /etc/bash.bashrc && \
374412 echo 'cat /opt/dynamo/.launch_screen' >> /etc/bash.bashrc
375413
376414USER dynamo
377415
378416# Copy tests, benchmarks, deploy and components for CI with correct ownership
379- COPY --chown=dynamo: pyproject.toml /workspace/
380- COPY --chown=dynamo: tests /workspace/tests
381- COPY --chown=dynamo: examples /workspace/examples
382- COPY --chown=dynamo: benchmarks /workspace/benchmarks
383- COPY --chown=dynamo: deploy /workspace/deploy
384- COPY --chown=dynamo: components/ /workspace/components/
385- COPY --chown=dynamo: recipes/ /workspace/recipes/
386-
387417ARG DYNAMO_COMMIT_SHA
388418ENV DYNAMO_COMMIT_SHA=$DYNAMO_COMMIT_SHA
389419
@@ -420,6 +450,8 @@ ENV VIRTUAL_ENV=/opt/dynamo/venv \
420450 PATH="/opt/dynamo/venv/bin:${PATH}"
421451
422452USER root
453+ # venv permissions are handled by umask 002 set earlier
454+
423455# Install development tools and utilities
424456RUN apt-get update -y && \
425457 apt-get install -y --no-install-recommends \
@@ -478,7 +510,7 @@ RUN curl --retry 3 --retry-delay 2 -LSso /usr/local/bin/clang-format https://git
478510 && rm -rf clangd_18.1.3 clangd.zip
479511
480512# Editable install of dynamo
481- COPY README.md hatch_build.py /workspace/
513+ COPY --chmod=664 pyproject.toml README.md hatch_build.py /workspace/
482514RUN python3 -m pip install --no-deps -e .
483515
484516# Install Python development packages
@@ -496,4 +528,4 @@ RUN python3 -m pip install --no-cache-dir \
496528 tabulate
497529
498530ENTRYPOINT ["/opt/nvidia/nvidia_entrypoint.sh"]
499- CMD []
531+ CMD []
0 commit comments