From mise-toolkit
The builder → runtime multi-stage split for mise-based Dockerfiles — what to copy across stages, when mise itself belongs in the runtime stage, and how to keep the runtime image small without losing reproducibility.
npx claudepluginhub ray-manaloto/claude-code-marketplace --plugin mise-toolkitThis skill uses the workspace's default tool permissions.
Multi-stage is not optional for production Docker images with mise. The mise install tree (~/.local/share/mise/installs/) is easily 200MB-1GB of compilers, sources, and runtime libs you don't want in your deployed image.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Guides code writing, review, and refactoring with Karpathy-inspired rules to avoid overcomplication, ensure simplicity, surgical changes, and verifiable success criteria.
Share bugs, ideas, or general feedback.
Multi-stage is not optional for production Docker images with mise. The mise install tree (~/.local/share/mise/installs/) is easily 200MB-1GB of compilers, sources, and runtime libs you don't want in your deployed image.
Think of mise as a build-time concern, not a runtime concern. mise's job is to give you pinned, reproducible versions of node, python, go, cargo, etc. while you're building the app. Once the app is built, you ship the app — not the toolchain.
┌──────────────────┐ ┌──────────────────┐
│ builder stage │ │ runtime stage │
│ │ │ │
│ + mise │ COPY │ + app artifact │
│ + installed tools│ ──────▶ │ + runtime libs │
│ + source code │ │ + non-root user │
│ + build output │ │ │
└──────────────────┘ └──────────────────┘
(big, discarded) (small, shipped)
Copy just the binary. The runtime doesn't need mise, Go, or cargo at all.
FROM debian:12-slim AS runtime
COPY --from=builder /workspace/target/release/myapp /usr/local/bin/myapp
This is the cleanest case and the biggest size win. A 1GB builder stage becomes a 100MB runtime stage trivially.
Copy the app directory plus node_modules / the virtualenv / the lock file. Then copy the specific interpreter binary.
FROM debian:12-slim AS runtime
# Copy the interpreter from mise's install tree
COPY --from=builder /root/.local/share/mise/installs/node/20.11.0 /usr/local/node
ENV PATH="/usr/local/node/bin:${PATH}"
WORKDIR /app
COPY --from=builder /workspace/node_modules ./node_modules
COPY --from=builder /workspace/dist ./dist
COPY --from=builder /workspace/package.json ./
CMD ["node", "dist/server.js"]
This is the surgical pattern — you ship exactly one interpreter version, no mise, no build tools. ~150MB total.
Copy the whole mise tree. Simplest, biggest.
FROM debian:12-slim AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/bin/mise /usr/local/bin/mise
COPY --from=builder /root/.local/share/mise /home/dev/.local/share/mise
COPY --from=builder /workspace/mise.toml /workspace/mise.lock /app/
RUN useradd -m -u 1000 dev && chown -R dev:dev /home/dev/.local /app
USER dev
WORKDIR /app
COPY --from=builder --chown=dev:dev /workspace/dist ./dist
COPY --from=builder --chown=dev:dev /workspace/node_modules ./node_modules
ENV MISE_TRUSTED_CONFIG_PATHS=/app
ENV PATH="/home/dev/.local/share/mise/shims:${PATH}"
CMD ["node", "dist/server.js"]
Use this when:
mise run <task> as the container entrypoint.docker exec + running mise commands.dist/ or equivalent, not the whole repo).node_modules from the builder if they include dev deps. Install with npm ci --omit=dev in the builder, or prune in a separate RUN step before the stage boundary.~/.cache, ~/.npm, ~/.cargo/registry). These are the whole point of using a BuildKit cache mount — they stay in the builder, never in the final image.apt lists (/var/lib/apt/lists/*) — always rm -rf after install.Use explicit names, not numbers:
FROM debian:12-slim AS base # shared setup
FROM base AS builder # adds mise + tools + source
FROM base AS runtime # clean, gets just the artifact
A base stage for shared apt installs deduplicates the apt-get update && install ca-certificates step.
"My CI is simple, I just have one big stage" is fine for development, but:
docker pull in prod downloads all that.Multi-stage is the most impactful refactor you can do to a Dockerfile. If you're going to adopt mise in Docker, adopt multi-stage in the same PR.
mise-docker-patterns — the end-to-end canonical Dockerfile.mise-docker-base-images — picking the base for builder and runtime (they don't have to match).mise-docker-bootstrap — pinning the mise version in the builder.docs.docker.com/build/building/multi-stage/.