Skip to content

Pin @swc/core so the app boots read-only, and boot the image in PR CI - #144

Merged
heliocosta-dev merged 2 commits into
mainfrom
fix/swc-native-cache
Oct 17, 2026
Merged

heliocosta-dev merged 2 commits into
mainfrom
fix/swc-native-cache

Conversation

@heliocosta-dev

Copy link
Copy Markdown
Member

v0.15.0's app container restart-loops at boot, so this is the hotfix for v0.15.1.

What fails

next start loads next.config.ts, which loads next-intl's plugin, and next-intl loads @swc/core. #139 moved @swc/core from 1.16.2 to 1.16.18 through next-intl's ~1.16.0 range. From 1.16.12, @swc/core unpacks its native addon into a user cache directory on first load and refuses to load without one it can write and execute from:

Error: SWC native addon: user cache failed: create cache root /home/millionsend/.cache: Read-only file system (os error 30); fallback cache failed: native addon user cache /tmp is mounted noexec

The compose files run the app with a read-only root filesystem, as a non-root user, with Docker's default (noexec) tmpfs at /tmp. So web exits, and the start script stops the api and the worker with it.

The fix

  • Pin @swc/core to 1.16.2 with an override in pnpm-workspace.yaml, the version v0.14.0 shipped. The lockfile changes only the swc packages, and their integrity hashes match v0.14.0's. next-intl 4.14.9 declares ~1.16.0, so 1.16.2 is in range.

  • Boot the image in pull request CI the way the compose files run it. The image job now loads the app image and runs it with:

    • a read-only root filesystem, the non-root user, and --cap-drop ALL / no-new-privileges;
    • the same tmpfs paths;
    • a throwaway Postgres.

    The step passes once /login and the API's /health both answer; otherwise it prints the container's logs. A dependency that needs to write or execute outside those mounts now fails here, before a release.

Verified

  • In a Linux build with these exact flags, the pinned tree booted in 5 seconds: /login 200, /health ok, and api, worker and web all stayed up.
  • With 1.16.18 swapped back in, the same boot failed with the error above.
  • 1.16.12, 1.16.13, 1.16.17 and 1.16.18 all fail; 1.16.2 loads.
  • pnpm lint, pnpm typecheck and the apps/web suite (1277 tests) pass.

🤖 Generated with Claude Code

heliocosta-dev and others added 2 commits October 17, 2026 09:51
next.config.ts loads next-intl's plugin, which requires @swc/core at
load time. From 1.16.12, @swc/core's native addon unpacks itself into a
cache directory on first load (~/.cache, then /tmp) and refuses to load
unless that directory is writable and executable. A container with a
read-only root filesystem and a noexec /tmp has neither, so `next start`
throws ERR_SWC_NATIVE_CACHE while loading the config; the web process
exits and the start script takes the api and worker down with it.

1.16.2 is the last release that loads its addon in place, and the one
the lockfile resolved before. It is inside next-intl 4.14.9's declared
range (~1.16.0). Pointing SWC_NATIVE_BINDING_CACHE at an executable
tmpfs would also work, but every deployment's compose file would need
that mount; the pin fixes the image itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image job only built the image, so nothing ran it the way the
compose files do: read-only root filesystem, non-root user, and
Docker's default tmpfs mounts, which are noexec. A dependency that
needs a writable or executable path outside those mounts passed CI and
failed only once deployed, as @swc/core does from 1.16.12.

The job now loads the image and starts it with the compose services'
runtime flags, the same tmpfs paths, a throwaway Postgres and the four
variables a boot needs (APP_BASE_URL included: auth refuses to start
without it in production mode). It passes once the dashboard's /login
and the API's /health both answer, and fails with the container's logs
if the container stops or two minutes pass first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@heliocosta-dev
heliocosta-dev merged commit a5de3cc into main Oct 17, 2026
5 checks passed
@heliocosta-dev
heliocosta-dev deleted the fix/swc-native-cache branch October 17, 2026 13:16
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.

1 participant