[2.8] Fix Docker SJ workspace tmpfs permissions#4574
Conversation
e86e9e3 to
ce1db5a
Compare
ce1db5a to
2fabdc2
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes a Docker-mode Server Job (SJ) startup failure by making the SJ/CJ workspace tmpfs mount writable (sticky 1777), while preserving the existing isolation model where startup/ and local/ are read-only and only the per-job directory is host-backed writable storage. It also updates documentation and examples to reflect the intended ephemeral vs persistent storage behavior.
Changes:
- Change the Docker launcher’s workspace tmpfs mode from
0555to sticky world-writable1777so non-root job containers can create required top-level ephemeral directories. - Update the Docker launcher unit test expectation to match the new tmpfs permissions.
- Update Docker design docs and the Docker example README to better describe/reflect workspace behavior and commands.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
nvflare/app_opt/job_launcher/docker_launcher.py |
Makes the SJ/CJ workspace tmpfs root writable (1777) and clarifies the workspace isolation behavior in comments/docstring. |
tests/unit_test/app_opt/job_launcher/docker_launcher_test.py |
Updates mount expectation to assert tmpfs_mode: 0o1777. |
examples/docker/README.md |
Adjusts example commands to be more copy/pasteable and changes the assumed working directory. |
docs/design/docker_job_launcher_design.md |
Updates the design doc to describe the new tmpfs permissions and ephemeral storage implications for SJ/CJ containers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Greptile SummaryThis PR fixes a permissions bug in the Docker job launcher where SJ/CJ containers failed to start because
Confidence Score: 5/5Safe to merge — the change is a targeted, well-scoped one-liner fix with an updated test and consistent documentation. The single code change (tmpfs mode constant) is the minimal correct fix for the EACCES boot failure. The sticky 1777 mode is the established Linux standard for a shared writable scratch space; the tmpfs is container-local and ephemeral so the broader permission does not increase the host or cross-container attack surface. The unit test was updated in lock-step, and both the design doc and README accurately describe the new behavior. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant SP as SP/CP Container
participant DL as DockerJobLauncher
participant D as Docker Daemon
participant SJ as SJ/CJ Container
SP->>DL: launch_job(job_id, workspace)
DL->>D: "containers.run(mounts=[...])"
Note over DL,D: Mount 1: tmpfs at /var/tmp/nvflare/workspace (mode=0o1777, ephemeral)
Note over DL,D: Mount 2: bind startup/ → read-only
Note over DL,D: Mount 3: bind local/ → read-only
Note over DL,D: Mount 4: bind job_workspace/ → read-write (host-backed)
D->>SJ: start container
SJ->>SJ: snapshot_persistor creates /var/tmp/nvflare/workspace/snapshot-storage
SJ->>SJ: job_manager creates /var/tmp/nvflare/workspace/jobs-storage
SJ->>SJ: writes job outputs to /var/tmp/nvflare/workspace/job_id/ (persisted on host)
SJ-->>SP: job complete
Reviews (1): Last reviewed commit: "Fix Docker README config paths" | Re-trigger Greptile |
## Summary - make the Docker job launcher workspace tmpfs writable with sticky `1777` permissions - keep `startup/` and `local/` read-only and the current job directory as the only host-backed writable mount - update the Docker launcher unit expectation and design doc to describe the ephemeral SJ/CJ storage behavior - make the Docker example startup commands copy/pasteable by backgrounding parent processes with per-site logs - update the Docker example job submit command to use `--startup-kit` - make Docker example paths consistently relative to `examples/docker` ## Root Cause `deploy prepare` rewrites server storage paths to `/var/tmp/nvflare/workspace/snapshot-storage` and `/var/tmp/nvflare/workspace/jobs-storage`. Server job containers inherit those resources, but the Docker job launcher mounted `/var/tmp/nvflare/workspace` as a read-only-style `0555` tmpfs. During SJ boot, `snapshot_persistor` and `job_manager` both try to create their storage directories under the tmpfs root and fail with `EACCES`. ## Impact SJ/CJ containers still do not receive the parent site workspace bind mount. The top-level workspace root is now writable only as ephemeral tmpfs so startup-time storage initialization can succeed, matching the K8s emptyDir behavior more closely. Host-backed writes remain limited to `/var/tmp/nvflare/workspace/<job_id>`. (cherry picked from commit 949dcf3)
## Summary Port the selected 2.8 fixes back to `main` in 2.8 merge order: - #4528 Add warnings for missing study data mappings - #4538 Update deploy prepare launcher docs - #4550 Align `Run.get_result()` with the `clean_up` parameter spelling - #4561 Clarify `remove_client` token cleanup semantics - #4563 Respect `CUDA_VISIBLE_DEVICES` in the GPU resource manager - #4574 Fix Docker SJ workspace tmpfs permissions - #4576 Narrow client failure reporting for generic launcher execution errors - #4583 Fix tracking recipe integration test --------- Signed-off-by: YuanTingHsieh <yuantingh@nvidia.com>
Summary
1777permissionsstartup/andlocal/read-only and the current job directory as the only host-backed writable mount--startup-kitexamples/dockerRoot Cause
deploy preparerewrites server storage paths to/var/tmp/nvflare/workspace/snapshot-storageand/var/tmp/nvflare/workspace/jobs-storage. Server job containers inherit those resources, but the Docker job launcher mounted/var/tmp/nvflare/workspaceas a read-only-style0555tmpfs. During SJ boot,snapshot_persistorandjob_managerboth try to create their storage directories under the tmpfs root and fail withEACCES.Impact
SJ/CJ containers still do not receive the parent site workspace bind mount. The top-level workspace root is now writable only as ephemeral tmpfs so startup-time storage initialization can succeed, matching the K8s emptyDir behavior more closely. Host-backed writes remain limited to
/var/tmp/nvflare/workspace/<job_id>.