Skip to content

fix: stop shipping a second copy of the toolchain in every Railpack image - #4

Open
jeroenrinzema wants to merge 2 commits into
brainpodnl:mainfrom
jeroenrinzema:fix/image-mise-chown-duplication
Open

fix: stop shipping a second copy of the toolchain in every Railpack image#4
jeroenrinzema wants to merge 2 commits into
brainpodnl:mainfrom
jeroenrinzema:fix/image-mise-chown-duplication

Conversation

@jeroenrinzema

Copy link
Copy Markdown
Contributor

Every Railpack image we build ships a byte-identical second copy of the toolchain. A stock create-next-app is 1163 MB on disk; 194 MB of that is the duplicate.

Cause

The non-root layer ended with:

&& if [ -d /mise ]; then chown -R 1000:1000 /mise; fi

/mise arrives from an earlier layer. chown rewrites every file it touches, so BuildKit records the entire tree again in the layer users pull — same bytes, different owner. It is the single largest layer we add and it contributes nothing.

compressed our non-root layer
before 357.5 MB 58.7 MB
after 298.8 MB 0.0 MB

Why the chown was unnecessary

/mise/installs is already drwxr-xr-x root:root, so uid 1000 has the read and execute it actually uses.

What mise does need is somewhere to write. Drop the chown outright and it prints [WARN] migrate: failed create_dir_all: /mise/migrations on every start — non-fatal, but it lands in the user's logs. It wants exactly two directories, and MISE_CACHE_DIR=/mise/cache is in the runtime env while that directory does not exist at all. Creating both empty costs nothing measurable and leaves no warning.

Verification

Built through the same path the CLI drives (Railpack v0.35.0, linux/arm64, same frontend and plan), on two stacks so this is not a Node-only claim:

stack before after
create-next-app (Next 16.3.0) 1163 MB 969 MB
Flask + gunicorn (Python 3.13) 407 MB 328 MB

For each, as uid=1000(railpack):

  • app serves HTTP 200 (Next.js also serves /_next/static/…css)
  • node/npm and python/pip resolve through /mise/shims
  • mise exec -- … and mise ls succeed
  • no permission-denied or create_dir_all warning remains

The layout.runtime_user == 1000:1000 assertion in build() still holds — USER 1000:1000 is unchanged, so the guard against a root image is untouched.

Not addressed here

cp -a /root/. /home/railpack/ leaves two copies of whatever Railpack puts in /root (it includes /root/.cache in the deploy step). That was ~7 MB on both stacks, and unlike the chown the copy is doing real work — the runtime user's HOME moves — so it needs a different fix than deleting a line.

Separately: Railpack v0.35.0 ignores Next.js output: 'standalone' — the generated deploy plan is byte-identical with and without it, and /app/node_modules ships unconditionally. Shrinking that is a railpack.json matter for the app, not a CLI change.

…mage

The non-root layer ended with `chown -R 1000:1000 /mise`. /mise arrives from
an earlier layer, so rewriting its ownership makes BuildKit record every file
in it again: a byte-identical duplicate of the whole toolchain, in the layer
users pull. On a stock create-next-app that is 194 MB on disk and 58.7 MB
across the wire, for no change in content.

It was never needed. /mise/installs is drwxr-xr-x root:root, so uid 1000
already has the read and execute it uses. What mise does want at runtime is
somewhere to write, and it takes exactly two directories -- without them it
prints `migrate: failed create_dir_all: /mise/migrations` on every start.
Create those two instead of taking ownership of the tree.

Measured on linux/arm64 against Railpack v0.35.0, uncompressed image size:

    create-next-app   1163 MB -> 969 MB
    Flask + gunicorn   407 MB -> 328 MB

Both run as uid=1000(railpack) and serve 200. Verified node, npm, python and
pip resolve through /mise/shims, that `mise exec` and `mise ls` work, and that
no permission warning is left on either.
Leaves only the warning that has to survive: chowning /mise duplicates
the toolchain, which is the mistake this fix exists to prevent being
reintroduced.
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