fix(docker): make venv writes group-writable to survive APP_UID drift - #844
fix(docker): make venv writes group-writable to survive APP_UID drift#844Ahmath-Gadji wants to merge 1 commit into
Conversation
The openrag_venv named volume persists across container recreations, but the openrag image's non-root APP_UID can differ between a locally built image (defaults to the host UID) and a pulled/CI-built image (defaults to 10001) even though both share the same image tag. The default umask (022) makes `uv sync` create new venv entries writable only by the exact UID that wrote them, so a later sync under a different APP_UID fails to remove/replace files (e.g. the editable install's __editable__*.pth on every version bump) with "Permission denied". Force umask 002 before running uv so venv entries stay group (GID 0) writable, letting any APP_UID that shares that group resync.
📝 WalkthroughWalkthroughThe entrypoint now sets ChangesStartup permissions
Estimated code review effort: 1 (Trivial) | ~2 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@infra/scripts/entrypoint.sh`:
- Around line 33-40: Update the Ray init-container command in the ray cluster
template to set umask 002 immediately before its uv sync invocation, matching
the setting in entrypoint.sh. Ensure this direct sync path creates
group-writable venv entries so resynchronization under different GID-0 UIDs
remains possible.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 92828c79-6f7c-4369-832f-2e21bf547ce8
📒 Files selected for processing (1)
infra/scripts/entrypoint.sh
| # The persisted openrag_venv volume can be re-synced by different APP_UIDs | ||
| # across the container's lifetime (e.g. a locally-built image bakes in the | ||
| # host UID while a pulled/CI-built image bakes in the Dockerfile default) — | ||
| # both share GID 0. Default umask (022) makes `uv sync` create new venv | ||
| # entries owner-writable only, so a later sync under a different UID can't | ||
| # remove/replace them ("Permission denied" on __editable__*.pth). Force | ||
| # group-writable new files/dirs so any GID-0 UID can always resync. | ||
| umask 002 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n -C 6 '\buv sync\b|\bumask\b|command:' \
infra/scripts/entrypoint.sh \
infra/charts \
infra/dockerRepository: linagora/openrag
Length of output: 11846
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '--- entrypoint outline/slice ---\n'
wc -l infra/scripts/entrypoint.sh
sed -n '1,90p' infra/scripts/entrypoint.sh | cat -n
printf '\n--- raycluster relevant slices ---\n'
wc -l infra/charts/openrag-stack/templates/raycluster.yaml
sed -n '1,70p' infra/charts/openrag-stack/templates/raycluster.yaml | cat -n
sed -n '115,145p' infra/charts/openrag-stack/templates/raycluster.yaml | cat -n
printf '\n--- chart values/security/default context references ---\n'
rg -n "mergeSecurityContext|podSecurityContext|containerSecurityContext|APP_UID|openrag_venv|venv" infra/charts openrag charts . -g'*.yaml' -g'*.tpl' -g'*.sh' | head -200Repository: linagora/openrag
Length of output: 245
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- entrypoint outline/slice ---'
wc -l infra/scripts/entrypoint.sh
sed -n '1,120p' infra/scripts/entrypoint.sh | cat -n
printf '%s\n' ''
printf '%s\n' '--- raycluster relevant slices ---'
wc -l infra/charts/openrag-stack/templates/raycluster.yaml
sed -n '1,80p' infra/charts/openrag-stack/templates/raycluster.yaml | cat -n
sed -n '115,145p' infra/charts/openrag-stack/templates/raycluster.yaml | cat -n
printf '%s\n' ''
printf '%s\n' '--- chart values/security/context references ---'
rg -n "mergeSecurityContext|podSecurityContext|containerSecurityContext|APP_UID|openrag_venv|venv|initContainers" infra/charts/openrag-stack -g'*.yaml' -g'*.tpl' | head -200Repository: linagora/openrag
Length of output: 20441
🌐 Web query:
uv sync __editable__*.pth permissions Python virtual environment ownership
💡 Result:
In the uv ecosystem, editable*.pth files are standard mechanisms used for editable installations to extend sys.path [1][2]. If you encounter permission errors regarding these files or the.venv directory itself, it typically stems from ownership mismatches—often when a virtual environment is created by a privileged user (e.g., root in Docker) and subsequently accessed by an unprivileged user, or when a file system is read-only [3][4][5]. Permission issues in these contexts generally arise for the following reasons: Ownership and Docker: If uv sync is executed as root during a container build, the resulting.venv and its contents (including the.pth files) are owned by root [3]. Subsequent processes running as a non-privileged user will lack the necessary permissions to modify or, in some cases, properly interact with these files [3][6][7]. To resolve this, you should ensure the virtual environment directory is owned by the user who will be running the application, for instance by using chown after the installation step or by running the uv commands as the intended user [3][7]. Read-Only Filesystems: Python's site module attempts to process.pth files during interpreter initialization [1][5]. If the virtual environment resides on a read-only volume, this attempt will fail [5]. In such environments, you may need to suppress the automatic site initialization using the -S flag (e.g., uv run python -S ...), though this may affect how other packages are loaded [5]. Hidden Filesystem Flags: On macOS, if the.venv directory or the.pth files are marked with the UF_HIDDEN flag, modern Python versions (3.12+) will silently skip them, breaking the editable installation [8]. Verifying file attributes with ls -lO can help diagnose this [8]. To avoid permission issues, it is recommended to manage the virtual environment's creation and ownership explicitly within your workflows, ensuring the user executing the application has full read/write/execute access to the.venv directory [3][9]. If you are working in Docker, consider creating the directory and performing the sync as the non-privileged user, or adjusting permissions after the fact [3][7].
Citations:
- 1: Unexpected sys.path Behavior When Using uv run python with Editable Install in a Docker Container astral-sh/uv#13454
- 2: document that the uv build backend uses
.pthfiles for editable installs by default astral-sh/uv#15652 - 3: https://stackoverflow.com/questions/79787507/changing-the-owner-of-venv-created-by-uv-inside-docker
- 4: uv sync: empty directories are assumed to be linked to non-existent interpreters astral-sh/uv#7212
- 5: how to get uv to run my app on a read-only volume? astral-sh/uv#10687
- 6: Permission Denied: Running UV Installed Python with Non-Root User astral-sh/uv#13309
- 7: Building docker image and then switching to non root user astral-sh/uv#7758
- 8: Local packages can become unimportable due to skipped hidden
.pthfiles astral-sh/uv#16977 - 9: UV_SYSTEM_PYTHON and UV_PROJECT_ENVIRONMENT not work for not privileged users in docker (pip install --user mode) astral-sh/uv#13107
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- Dockerfile user setup and app UID defaults ---'
fd -i 'Dockerfile' . -x sh -c 'printf "\n### %s\n" "$1"; rg -n -C 3 "^USER|^ENV .*UID|APP_UID|workdir|COPY infra/scripts/entrypoint|chmod g=u|chmod .7|chmod g=u|groupadd|useradd" "$1" || true' sh {}
printf '%s\n' ''
printf '%s\n' '--- persistence/venv chart references ---'
sed -n '1,130p' infra/charts/openrag-stack/templates/pvc.yaml | cat -n
sed -n '1,80p' infra/charts/openrag-stack/values.yaml | cat -n
sed -n '220,235p' infra/charts/openrag-stack/values.yaml | cat -n
printf '%s\n' ''
printf '%s\n' '--- all containers/volumes using .venv in openrag-stack templates ---'
rg -n -C 3 "mountPath: /app/\.venv|claimName: .*venv|/app/\.venv|openrag_venv" infra/charts/openrag-stack/templates -g'*.yaml'Repository: linagora/openrag
Length of output: 18613
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- ray Dockerfile full setup section ---'
sed -n '1,75p' infra/docker/ray.Dockerfile | cat -n
printf '%s\n' ''
printf '%s\n' '--- ray env/config references ---'
rg -n -C 3 "APP_UID|APP_iPORT|KUBERAY|entrypoint|raycluster|openrag-stack.fullname|env:" infra/charts/openrag-stack/templates/raycluster.yaml infra/charts/openrag-stack/values.yamlRepository: linagora/openrag
Length of output: 15965
Apply the umask to the Ray init-container sync path.
infra/scripts/entrypoint.sh sets umask 002, but the Ray init container in infra/charts/openrag-stack/templates/raycluster.yaml runs uv sync directly and does not inherit this setting. A later uv sync under a different UID can then fail when removing or replacing owner-only editable venv entries. Add umask 002 before uv sync in the init-container command, or use a shared startup helper for both sync paths.
Proposed chart fix
- |
+ umask 002
if [ -f /app/.venv/.ready ]; then🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infra/scripts/entrypoint.sh` around lines 33 - 40, Update the Ray
init-container command in the ray cluster template to set umask 002 immediately
before its uv sync invocation, matching the setting in entrypoint.sh. Ensure
this direct sync path creates group-writable venv entries so resynchronization
under different GID-0 UIDs remains possible.
Summary
openrag_venvnamed volume persists across container recreations, but its non-rootAPP_UIDcan differ between a locally built image (defaults to the host UID via compose's build arg) and a pulled/CI-built image (defaults to the Dockerfile's own10001) — even when both share the same image tag.uv synccreate new venv entries writable only by the exact UID that wrote them, so a later sync under a differentAPP_UIDcan't remove/replace existing files (e.g.__editable__.openrag-X.Y.Z.pth, which changes on every version bump), failing withPermission denied.umask 002before invokinguv runin the entrypoint so all future venv writes stay group (GID 0) writable, letting anyAPP_UIDsharing that group resync without wiping the volume.Note: this only prevents the issue going forward. Anyone hitting this today still needs a one-time
docker volume rmof theiropenrag_venvvolume to clear already-poisoned owner-only files.Test plan
openrag_venvunder oneAPP_UID(e.g. local build with host UID), then re-sync under a differentAPP_UID(e.g. a pulled image or differentAPP_UIDbuild arg) and confirm noPermission deniedonuv sync.docker compose up) still works unchanged.Summary by CodeRabbit