Skip to content

feat: add buildMode "docker" — one isolated container per site#8

Merged
vianmora merged 4 commits into
mainfrom
feat/buildmode-docker
May 7, 2026
Merged

feat: add buildMode "docker" — one isolated container per site#8
vianmora merged 4 commits into
mainfrom
feat/buildmode-docker

Conversation

@vianmora

@vianmora vianmora commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Closes #5

What this adds

A new buildMode: "docker" for POST /publish. Instead of running a raw Node subprocess, each site gets its own isolated Docker container. The publisher builds the image once and routes all hostnames (publish domain + custom domains) to it via the existing proxy.

How it works

POST /publish { buildId, builderOrigin, buildMode: "docker" }
  → webstudio sync + build --template docker
  → patches generated [_image].$.ts and writes a NavLink fix script
  → writes optimized multi-stage Dockerfile (BuildKit cache mounts)
  → docker build -t ws-<domain> .
  → docker stop/rm ; docker run -d --restart=unless-stopped
  → docker image prune -f
  → all hostnames registered in proxy

On publisher restart, existing Docker containers are detected via docker inspect and restarted if stopped. Mode transitions (SSG↔Docker) clean up the previous state before building.

Why this is useful for self-hosters

  • Isolation — a crash or memory leak on one site doesn't affect others
  • Restart resilience--restart=unless-stopped survives host reboots without publisher involvement
  • Operational claritydocker ps shows all sites; docker logs <name> per site
  • Faster rebuilds — multi-stage Dockerfile with BuildKit npm cache mounts: packages are not re-downloaded on republish

Infra requirement

The publisher container needs access to the host Docker daemon:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

A warning is logged at startup if the socket is not accessible.

Bug fixes included

SSG — stale pages after rename (fix(ssg))

When a page is renamed or deleted in the builder, webstudio build --template ssg updates __generated__/ but leaves orphaned +Page.tsx files in pages/. Vite then fails to resolve the broken import. Fix: clean pages/ and app/ before each build.

Docker — images not loading (fix(docker): fix IPX image loading)

Two issues prevented images from loading in Docker SSR containers:

  1. The npm CLI template generates ipxFSStorage({ dir: "./public" }), but public/ is absent from the final multi-stage image — Vite moves assets to build/client/ at build time. The publisher patches [_image].$.ts after webstudio build to use the correct path.
  2. Passing a DOMAINS allowlist to IPX blocked images served from the public builder URL (403 IPX_FORBIDDEN_HOST). Fix: pass IPX_HTTP_ALLOW_ALL_DOMAINS=true to the container instead.

Docker — hover styles always active on hash anchor links (fix(docker): NavLink aria-current)

In SSR, all /#section links got aria-current="page" because they share the pathname / with every root page. NavLink with end={true} still marks them all as active.

The fix is in @webstudio-is/sdk-components-react-router/src/link.tsx (fork PR pending), but the npm CLI installs the upstream package so the fix doesn't apply automatically. The publisher writes a patch-navlink.cjs script into the workDir; the Dockerfile runs it after npm ci to patch the compiled lib/components.js before the React Router build.

@vianmora vianmora force-pushed the feat/buildmode-docker branch from 7011666 to 925a0de Compare April 27, 2026 16:57
@vianmora vianmora force-pushed the feat/buildmode-docker branch from 250e560 to 5b9ce3d Compare May 7, 2026 17:46
vianmora added 4 commits May 7, 2026 19:48
… imports

When a page is renamed or deleted in the builder, webstudio build --template ssg
updates __generated__/ but leaves orphaned +Page.tsx files in pages/. Vite then
fails to resolve the broken import from the stale page file.
IPX needs DOMAINS set to fetch assets from /cgi/image/... URLs.
Without it, all /_image/ requests return 404 in Docker mode.
…containers

The npm CLI generates code that installs @webstudio-is/sdk-components-react-router
from the upstream registry, so the fork fix in link.tsx doesn't apply automatically.

Write a patch-navlink.cjs script into the workDir before docker build. The Dockerfile
runs it after npm ci (inside the build container) to patch the compiled
lib/components.js before the React Router build — excluding /#section links from
NavLink so they don't all get aria-current="page" in SSR.
@vianmora vianmora force-pushed the feat/buildmode-docker branch from 5b9ce3d to 16a1546 Compare May 7, 2026 17:49
@vianmora vianmora merged commit a98001c into main May 7, 2026
@vianmora vianmora deleted the feat/buildmode-docker branch May 16, 2026 07:26
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.

feat: new buildMode "docker" — one Docker container per domain

1 participant