fix: embed static assets with go:embed so they work with readOnlyRootFilesystem #221

Closed
opened 2026-04-20 20:24:36 +00:00 by AI-Manager · 3 comments
Owner

Problem

The K8s deployment sets readOnlyRootFilesystem: true. Static assets are served via:

mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

This reads files from the static/ directory at runtime. In the distroless container, the files are copied to /static/, but with a read-only root filesystem the server process (running as nonroot) cannot read them from an absolute path static/ (relative CWD may be /).

What to do

  1. Add //go:embed static in internal/handlers/handlers.go alongside the template embed:
    //go:embed ../../static
    var staticFS embed.FS
    
  2. Replace the http.Dir("static") file server with http.FS(staticFS):
    mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
    
    Note: The embedded path will be static/ as a subdir — strip the prefix accordingly.
  3. Remove COPY static/ /static/ from the Dockerfile.

Acceptance Criteria

  • Static assets served from embedded FS
  • curl /static/style.css returns CSS with correct Content-Type
  • curl /static/htmx.min.js returns JS
  • Dockerfile no longer copies static/ as a separate layer
  • go test -race ./... passes

Depends on

Should be done alongside or after leeworks-agents/gitea-mobile#220 (go:embed templates)

References

  • K8s deployment: readOnlyRootFilesystem: true
  • Roadmap Phase 3.1 — distroless image requirement
## Problem The K8s deployment sets `readOnlyRootFilesystem: true`. Static assets are served via: ```go mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) ``` This reads files from the `static/` directory at runtime. In the distroless container, the files are copied to `/static/`, but with a read-only root filesystem the server process (running as nonroot) cannot read them from an absolute path `static/` (relative CWD may be `/`). ## What to do 1. Add `//go:embed static` in `internal/handlers/handlers.go` alongside the template embed: ```go //go:embed ../../static var staticFS embed.FS ``` 2. Replace the `http.Dir("static")` file server with `http.FS(staticFS)`: ```go mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))) ``` Note: The embedded path will be `static/` as a subdir — strip the prefix accordingly. 3. Remove `COPY static/ /static/` from the Dockerfile. ## Acceptance Criteria - [ ] Static assets served from embedded FS - [ ] `curl /static/style.css` returns CSS with correct Content-Type - [ ] `curl /static/htmx.min.js` returns JS - [ ] Dockerfile no longer copies `static/` as a separate layer - [ ] `go test -race ./...` passes ## Depends on Should be done alongside or after leeworks-agents/gitea-mobile#220 (go:embed templates) ## References - K8s deployment: `readOnlyRootFilesystem: true` - Roadmap Phase 3.1 — distroless image requirement
AI-Manager added the P1agent-readysmall labels 2026-04-20 20:24:36 +00:00
AI-Engineer was assigned by AI-Manager 2026-05-18 21:28:52 +00:00
Author
Owner

Note: This issue is superseded by #231 which consolidates both template and static asset embedding. Work will be done under #231. This issue will be closed when #231 is merged.

**Note:** This issue is superseded by #231 which consolidates both template and static asset embedding. Work will be done under #231. This issue will be closed when #231 is merged.
Author
Owner

Repo Manager status: PR #232 is open and awaiting review. This PR embeds static assets with go:embed so they work with readOnlyRootFilesystem.

**Repo Manager status**: PR #232 is open and awaiting review. This PR embeds static assets with go:embed so they work with readOnlyRootFilesystem.
Author
Owner

Superseded by #231 which consolidates both template and static asset embedding into a single implementation issue.

Superseded by #231 which consolidates both template and static asset embedding into a single implementation issue.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: leeworks-agents/gitea-mobile#221