Skip to content

[webserver] follow symlinks when serving static files#33853

Open
DRACULA1729 wants to merge 1 commit into
dagster-io:masterfrom
DRACULA1729:fix/webserver-follow-symlinks-staticfiles
Open

[webserver] follow symlinks when serving static files#33853
DRACULA1729 wants to merge 1 commit into
dagster-io:masterfrom
DRACULA1729:fix/webserver-follow-symlinks-staticfiles

Conversation

@DRACULA1729

Copy link
Copy Markdown
Contributor

Summary

Build environments such as Bazel materialize the bundled webapp/build/ tree by symlinking each individual file into a sandbox whose targets live outside the served directory. Starlette's StaticFiles rejects such symlinks by default — so dagster-webserver returns 404 Not Found for every static asset under Bazel and the UI loads as a blank page.

This regressed when build_static_routes moved from per-file Route(FileResponse(...)) (which transparently followed symlinks) to a directory Mount(StaticFiles(...)) in #21048.

Pass follow_symlink=True so the directory mount serves symlinked files normally. The served directory is curated by the build system, so this does not expand the exposure surface beyond what the previous FileResponse-per-file implementation already accepted.

Changes

  • dagster_webserver/webserver.py: add follow_symlink=True to the StaticFiles(...) call inside _static_dir. Inline comment links to the issue.
  • dagster_webserver_tests/webserver/test_app.py: add test_static_resources_follow_symlinks regression test that creates a tmp webapp/build/ with a symlinked CSS file whose target lives outside the served tree (mirroring Bazel's sandbox layout) and asserts the webserver serves it with status 200 and the correct body.

Test plan

  • New regression test fails on master without the patch (404 Not Found for the symlinked file).
  • New regression test passes with the patch.
  • Existing test_static_resources continues to pass.
  • ruff check / ruff format clean.
  • Test is skipped on Windows (creating symlinks requires elevated privileges there).

Fixes #33851

Build environments such as Bazel materialize the bundled `webapp/build/`
tree by symlinking each file into a sandbox whose targets live outside
the served directory. Starlette's `StaticFiles` rejects such symlinks by
default (since dagster-io#21048 switched from per-file `Route(FileResponse(...))`
to a directory `Mount(StaticFiles(...))`), so dagster-webserver serves
404s for every static asset under Bazel and the UI loads blank.

Pass `follow_symlink=True` so symlinked files are served normally. The
served directory itself is curated by the build system, so there is no
new exposure surface beyond what the previous `FileResponse`-per-file
implementation already accepted.

Add a regression test that mounts a tmp `webapp/build/` containing a
symlinked file whose target lives outside the served tree, mirroring
the Bazel sandbox layout from the issue.

Fixes dagster-io#33851
@greptile-apps

greptile-apps Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds follow_symlink=True to the StaticFiles(...) call in build_static_routes, restoring the symlink-following behavior that existed before the per-file FileResponse approach was replaced by a directory Mount. A targeted regression test mirrors the Bazel sandbox layout (symlink target outside the served tree) and verifies the fix end-to-end.

  • webserver.py: One-line change with an inline comment that links the issue — minimal and correct.
  • test_app.py: New test_static_resources_follow_symlinks test creates a tmp webapp/build/ with a symlink whose target lives outside the directory, subclasses DagsterWebserver to redirect relative_path, and asserts 200 OK with the correct body. Windows is correctly skipped.

Confidence Score: 5/5

Safe to merge — the change is a single-parameter addition to an internal static-file mount; the test directly validates the fix against the reported failure mode.

The production change is one line with no new logic paths; the only behavior difference is that symlinked files inside webapp/build/ now resolve instead of 404-ing. The new test faithfully reproduces the Bazel sandbox layout that caused the regression and confirms both the status code and response body.

No files require special attention.

Important Files Changed

Filename Overview
python_modules/dagster-webserver/dagster_webserver/webserver.py Adds follow_symlink=True to StaticFiles inside _static_dir; comment references the tracking issue. Change is minimal and correct.
python_modules/dagster-webserver/dagster_webserver_tests/webserver/test_app.py Adds test_static_resources_follow_symlinks regression test. Structure mirrors the Bazel sandbox layout and follows the same fixture/instance patterns established in conftest.py.

Sequence Diagram

sequenceDiagram
    participant Bazel as Bazel Sandbox
    participant BuildDir as webapp/build/static/
    participant Symlink as main.css (symlink)
    participant Target as ../outside/main.css (real file)
    participant StaticFiles as Starlette StaticFiles
    participant Client as HTTP Client

    Bazel->>BuildDir: create directory structure
    Bazel->>Symlink: os.symlink(target outside build/)
    Symlink-->>Target: points to real file outside served dir

    Client->>StaticFiles: GET /static/main.css
    StaticFiles->>Symlink: resolve path
    Note over StaticFiles,Symlink: follow_symlink=True allows this
    Symlink->>Target: follow symlink
    Target-->>StaticFiles: file contents
    StaticFiles-->>Client: 200 OK + file contents
Loading

Reviews (1): Last reviewed commit: "[webserver] follow symlinks when serving..." | Re-trigger Greptile

@DRACULA1729

Copy link
Copy Markdown
Contributor Author

Hi @SpencerWhitehead7, @smackesey, @alangenfeld — pinging you here since fork PRs can't formally request reviewers from outside the fork. Picked you specifically because:

Happy to expand the test or split the change if any of you would prefer a different approach. No rush — just flagging since the issue (#33851) has zero comments and I wasn't sure who watches this area.

@salazarm

salazarm commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

@gibsondan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dagster webserver doesn't return symlinked script files

2 participants