Docker Cookbook
Use the Debian image to run mise and install project tools, or copy the static mise binary into an existing image. To pin a small bootstrapper while allowing mise to float, install it with packslip. This cookbook also covers verified downloads and shared tool installations for development containers.
Official images
Release images are published for every stable release to ghcr.io/jdx/mise and Docker Hub (jdxcode/mise) for linux/amd64 and linux/arm64. Both registries use the same tags.
| Image | Example tags | Use it for |
|---|---|---|
| Scratch | 2026.9.11, 2026.9, latest | Copying the static musl binary into your own image |
| Debian slim | 2026.9.11-debian, 2026.9-debian, debian | Running mise in CI jobs and development containers |
The scratch image contains /usr/local/bin/mise and CA certificates. It has no shell or package manager. The Debian image uses debian:trixie-slim and includes the glibc mise binary, ca-certificates, curl, and git. Neither image preinstalls tools managed by mise.
Both images set MISE_DATA_DIR=/mise, MISE_CONFIG_DIR=/mise, and MISE_CACHE_DIR=/mise/cache, and add /mise/shims to PATH. The build verifies the release binaries against minisign-signed checksums before copying them into the images.
Use a full version tag to select a release. Month tags such as 2026.9 and floating tags (latest and debian) change as images are published. For an exact image, pin its digest.
Migrating from the previous image
latest now selects the scratch image. If you used the previous source-built image as a CI or development environment, switch to debian and install the tools your project needs. The old image remains available as dev for mise's internal tooling; it is unsupported for general use.
Copy the binary into your own image
Copy /usr/local/bin/mise from the scratch image into your Linux image. The statically linked binary runs on both glibc and musl systems, including Debian and Alpine. The destination still needs CA certificates for HTTPS and any OS dependencies required by your tools:
FROM debian:13-slim
COPY --from=ghcr.io/jdx/mise:2026.9.11 /usr/local/bin/mise /usr/local/bin/mise
RUN apt-get update \
&& apt-get -y --no-install-recommends install ca-certificates git \
&& rm -rf /var/lib/apt/lists/*
ENV MISE_DATA_DIR="/mise"
ENV MISE_CONFIG_DIR="/mise"
ENV MISE_CACHE_DIR="/mise/cache"
ENV PATH="/mise/shims:$PATH"COPY --from copies only the binary: it does not inherit the source image's certificates or environment variables. The example installs certificates and sets the mise directories explicitly.
Use the Debian image as a base
The Debian image has no ENTRYPOINT, so it works as a base image and lets CI runners supply their own shell command. This example assumes mise.toml declares Node.js and the application starts with node server.js:
FROM ghcr.io/jdx/mise:2026.9.11-debian
WORKDIR /app
# Also copy mise.lock if the project has one.
COPY mise.toml ./
RUN mise trust && mise install
COPY . .
CMD ["mise", "exec", "--", "node", "server.js"]Add OS packages required by your tools with apt-get. For GitLab CI, see the CI image example.
Pin an image by digest
A version tag selects a release, but a rebuilt image can change what that tag points to. To select an exact image, inspect its digest:
docker buildx imagetools inspect ghcr.io/jdx/mise:2026.9.11Replace <digest> below with the digest from that output:
COPY --from=ghcr.io/jdx/mise@sha256:<digest> /usr/local/bin/mise /usr/local/bin/miseThe same syntax works in FROM and CI image references. Inspect the -debian tag to get the Debian image's digest.
Installing project tools
Before building, exclude local credentials from the Docker build context with a .dockerignore file. Adapt these patterns to your project:
.env
.env.*
*.tfvars
*.tfvars.jsonTo install project tools as a build layer, copy the project config before its source files:
WORKDIR /app
COPY mise.toml ./
RUN mise trust && mise install
COPY . .Also copy mise.lock if the project uses a lockfile, plus any files the config reads. If an install hook needs application files, copy those before mise install. Keep credentials out of the build context rather than relying on later image layers to remove them. Use mise exec -- <command> or mise run <task> in RUN and CMD instructions; Docker build shells do not run interactive activation hooks.
Installing mise yourself
If you need to install mise directly into an existing base image, choose a package repository, packslip, a verified release download, a committed wrapper, or the install script below.
Bootstrap with packslip
Use packslip when you want to fix the verifier in a Dockerfile while allowing mise to follow its upstream releases. This separates the bootstrapper's version from the mise version you install. packslip verifies the release's signature, publisher, and archive bytes without running a mise install script.
This example pins packslip 1.5.1 by its multi-platform image digest and copies its CA bundle for HTTPS. packslip handles downloads and archive extraction, so the bootstrap needs no curl, tar, or additional package manager:
FROM ghcr.io/jdx/packslip:1.5.1@sha256:fcbbcb85ab02d433d6108c212ffc7eaeda0bbafca4b82111c452568ac680b9c4 AS bootstrap
FROM debian:13-slim
COPY --from=bootstrap /packslip /usr/local/bin/packslip
COPY --from=bootstrap /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ARG MISE_VERSION=latest
RUN packslip install github.com/jdx/mise --version "$MISE_VERSION" \
--pin ps1_nlhmwtfeufglxv5myvwvronk7a \
&& mise --version
ENV MISE_DATA_DIR="/mise"
ENV MISE_CONFIG_DIR="/mise"
ENV MISE_CACHE_DIR="/mise/cache"
ENV PATH="/mise/shims:$PATH"
CMD ["mise", "--version"]The digest pins packslip for both linux/amd64 and linux/arm64. The signer pin identifies mise's GitHub repository and stays valid as it publishes new releases. MISE_VERSION=latest lets mise float while packslip stays fixed. To fix mise as well, pass --build-arg MISE_VERSION=2026.10.1.
Build from a directory containing this Dockerfile:
docker build --no-cache -t mise-bootstrap .
docker run --rm mise-bootstrapDocker caches RUN layers, including the result of an installation that asks for latest. Rebuild without that cached layer when you want packslip to check for a new release. A running container does not automatically update mise. The stable manifest format lets you retain the bootstrapper across ordinary mise updates, but security or format changes may still require a packslip update.
Root installs mise's complete tree under /opt/packslip and exports a symlink in /usr/local/bin. If you move this installation into another build stage, copy the tree as well as the symlink. To install project tools, continue with Installing project tools and add their OS dependencies as needed. Use mise exec or mise run in containers; shell activation is not required.
Distribution packages
The apt, dnf, and apk installation methods use the package manager's signature verification. This Debian example uses extrepo to configure the mise apt repository:
# syntax=docker/dockerfile:1
FROM debian:13-slim
RUN <<EOF
set -ex
apt-get update
apt-get install -y extrepo
extrepo enable mise
apt-get remove -y --auto-remove extrepo # extrepo and its deps are not needed after extrepo enable
apt-get update
apt-get install -y mise
rm -fr /var/lib/apt/lists/*
EOFWith this approach you cannot choose the mise version with MISE_VERSION; pin it with apt version constraints instead.
Verified release download
Each release ships SHASUMS256.txt signed with minisign and GPG. This Debian example downloads the glibc binary for amd64 or arm64, verifies the checksum file with minisign, and checks the binary before installing it:
FROM debian:13-slim
ARG MISE_VERSION=2026.9.11
ARG MISE_MINISIGN_KEY=RWTC3g8W3z4RZK3V3qv7fa1QY4JEWyBtqIHW+85QlJpZc5yG+uNYNBSZ
RUN apt-get update \
&& apt-get -y --no-install-recommends install ca-certificates curl git minisign \
&& rm -rf /var/lib/apt/lists/*
RUN set -eux; \
base="https://github.com/jdx/mise/releases/download/v${MISE_VERSION}"; \
asset="mise-v${MISE_VERSION}-linux-$(dpkg --print-architecture | sed 's/amd64/x64/')"; \
cd /tmp; \
curl -fsSLO "$base/SHASUMS256.txt"; \
curl -fsSLO "$base/SHASUMS256.txt.minisig"; \
curl -fsSLO "$base/$asset"; \
minisign -Vm SHASUMS256.txt -P "$MISE_MINISIGN_KEY"; \
grep " ./$asset\$" SHASUMS256.txt | sha256sum -c --strict; \
install -m 755 "$asset" /usr/local/bin/mise; \
rm -f SHASUMS256.txt SHASUMS256.txt.minisig "$asset"The public key above is the mise release key from minisign.pub. Use the -musl asset for Alpine and other musl bases.
Committed wrapper
mise generate install-script -l -w writes a bin/mise wrapper from a signature-verified installer with embedded checksums. Commit the wrapper, copy it into the image, and call ./bin/mise to install and run its pinned version on first use. See Continuous integration.
Install script
The mise.run installer selects the platform and checks the binary's checksum. Set MISE_VERSION to pin the release:
FROM debian:13-slim
RUN apt-get update \
&& apt-get -y --no-install-recommends install \
# install any other dependencies you might need
curl git ca-certificates build-essential \
&& rm -rf /var/lib/apt/lists/*
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ENV MISE_DATA_DIR="/mise"
ENV MISE_CONFIG_DIR="/mise"
ENV MISE_CACHE_DIR="/mise/cache"
ENV MISE_INSTALL_PATH="/usr/local/bin/mise"
ENV PATH="/mise/shims:$PATH"
ENV MISE_VERSION="2026.9.11"
RUN curl --proto '=https' --proto-redir '=https' \
--fail --show-error --silent --location https://mise.run | shShared tools in multi-user containers
For toolbox containers or bastion hosts where tools should be pre-installed for all users, use mise install --system to install tools into /usr/local/share/mise/installs. Each user's mise finds these system-level tools automatically without any configuration.
--system shares the install location between users; it does not put binaries on PATH for use without mise. If you want tools other users can run with no mise involved, see How do I install tools other users can run without mise?
FROM ghcr.io/jdx/mise:debian
RUN apt-get update \
&& apt-get -y --no-install-recommends install build-essential \
&& rm -rf /var/lib/apt/lists/*
# Pre-install tools to the system-wide shared directory
RUN mise install --system node@26 python@3.15Users can inspect the shared installations with mise ls --installed. The versions below illustrate the output; patch versions depend on when the image was built:
$ mise ls --installed
node 26.0.0 (system)
python 3.15.0 (system)Users can install additional versions in their own directory — those take priority over system versions. When using an official image as the base, override its shared MISE_DATA_DIR, MISE_CONFIG_DIR, and MISE_CACHE_DIR for each user if you want private directories. To customize the system directory, set MISE_SYSTEM_DATA_DIR.
You can also configure additional shared directories with MISE_SHARED_INSTALL_DIRS (paths separated by : on Unix and ; on Windows) or the shared_install_dirs setting.
Devcontainers with home directory mounts
Devcontainers often mount the user's home directory, which means ~/.local/share/mise/installs comes from the mount rather than the Docker image. Tools pre-installed during docker build into ~/.local/share/mise/installs would be hidden by the mount.
Use mise install --system to install tools to /usr/local/share/mise/installs instead — this path is outside ~ and survives home directory mounts:
FROM debian:13-slim
# ... install mise ...
RUN mise install --system node@26 python@3.15When the container starts with ~ mounted, users still see the system tools automatically. Any tools they install normally go to ~/.local/share/mise/installs (on the mount) and take priority over system versions.
Overriding libc detection
In minimal Docker images (scratch, busybox, distroless) where no dynamic linker files exist, mise may not detect whether the system uses musl or glibc. Set libc or MISE_LIBC to override the detection:
ENV MISE_LIBC=musl
RUN mise installValid values are musl, glibc, and gnu (case-insensitive, with gnu treated as glibc). Invalid values are silently ignored, and mise falls back to runtime detection. When the mise binary is compiled for musl (the default for Linux releases), it also falls back to musl automatically when no linker is detected.
Task to run mise in a Docker container
This is useful for reproducing a mise issue in a clean environment.
[tasks.docker]
interactive = true
run = "docker run -it --rm ghcr.io/jdx/mise:debian bash"Then:
mise run dockerInside the disposable container, run mise doctor or create a small mise.toml that reproduces the issue. Activate mise with eval "$(mise activate bash)" only when testing interactive shell behavior. Exiting the shell removes the container because the task uses --rm.