Mise and Dev Containers – Simple Setup Guide
This guide shows how to use mise inside a Dev Container (works with DevPod and Visual Studio Code). It explains how to build a Dockerfile, write a setup script, connect chezmoi, and prepare Zsh.
The goal is to create a portable and ready-to-use development environment for your projects.
What Is Mise?
Mise is a simple tool manager that helps developers control versions of programming languages and tools across different projects.
It works like asdf or rtx. You can use it to install and switch between different versions of tools such as Node.js, Python, Go, Terraform, or smaller utilities like bat, fzf, or ripgrep.
Mise is very helpful when you work on several projects that need different versions of the same language or tool.
For example:
- Project A uses Python 3.10
- Project B needs Python 3.12
Instead of changing your system manually, mise automatically loads the correct version for each project using a file called mise.toml.
Why Is Mise Useful?
- It makes your development environment consistent on all machines.
- You can define tool versions inside
mise.tomland share it with your team. - It saves time when setting up new environments or containers.
- It avoids “it works on my computer” problems because everyone uses the same setup.
- It works perfectly with Dev Containers, chezmoi, and Zsh.
Goal of This Setup
- Build a Dev Container image that already includes mise
- Automatically install tools listed in your
mise.tomlfile - (Optional) Manage the mise binary with chezmoi for your local machine
- (Optional) Set up Zsh for a clean and friendly shell prompt
Project Structure (what each file is for)
project-root/
├─ .devcontainer/
│ ├─ devcontainer.json ← rules for building and starting your dev container
│ └─ Dockerfile ← how to build the container image (base OS, copy mise, etc.)
├─ scripts/
│ └─ setup ← runs after the container is created; installs tools with mise
├─ mise.toml ← list of tools and versions for this project
└─ (your source code) ← your app or library
Step 1: devcontainer.json (explain each field)
File: .devcontainer/devcontainer.json
{
"build": {
"context": "..",
"dockerfile": "Dockerfile"
},
"postCreateCommand": "scripts/setup"
}
What it means:
"build": tells Dev Containers to build a custom image."context": ".."means the build process can see the project root."dockerfile": "Dockerfile"means use theDockerfilein this folder.
"postCreateCommand": "scripts/setup"runs once after the container is created. It will trustmise.tomland install tools.
Why we build an image:
Having mise preinstalled makes the container start faster and work the same every time.
Step 2: Dockerfile (explain each line)
File: .devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
- Use a clean base image with Ubuntu 24.04 and default Dev Container tools.
COPY --from=jdxcode/mise /usr/local/bin/mise /usr/local/bin/
- Copy the
misebinary from the official mise image into our container. - Now
miseis available at/usr/local/bin/mise.
RUN echo 'eval "$(mise activate bash)"' >> /home/vscode/.bashrc \
&& echo 'eval "$(mise activate zsh)"' >> /home/vscode/.zshrc
- Automatically activate mise for both bash and zsh when the shell opens.
- If your user is not
vscode, update the home path.
Why this helps:
When you open a new terminal, mise loads the correct tool versions from mise.toml.
Step 3: scripts/setup (line-by-line explanation)
File: scripts/setup
Make it executable:
chmod +x scripts/setup
Script:
#!/usr/bin/env bash
set -euo pipefail
# Fail early if mise is missing
if ! command -v /usr/local/bin/mise >/dev/null 2>&1; then
echo "ERROR: mise not found at /usr/local/bin/mise" >&2
exit 1
fi
# Resolve workspace path (Dev Containers mount your repo under /workspaces/<name>)
WORKSPACE_DIR="${WORKSPACE_DIR:-${PWD}}"
# Point to your repo's mise.toml (adjust if stored elsewhere)
MISE_FILE="$WORKSPACE_DIR/mise.toml"
if [[ -f "$MISE_FILE" ]]; then
/usr/local/bin/mise trust "$MISE_FILE"
/usr/local/bin/mise install
else
echo "WARN: $MISE_FILE not found. Skipping mise install."
fi
# --- Optional: set zsh as default shell if available ---
if command -v zsh >/dev/null 2>&1; then
sudo chsh -s "$(command -v zsh)" "$USER" || true
fi
Explanation line by line:
#!/usr/bin/env bash– Runs the script using bash.set -euo pipefail– Makes the script stop when an error occurs or when a variable is missing.if ! command -v /usr/local/bin/mise >/dev/null 2>&1; then– Checks if mise is installed.echo "ERROR..."– If not, prints an error and stops the script.WORKSPACE_DIR="${WORKSPACE_DIR:-${PWD}}"– Sets the workspace path. If not defined, use the current folder.MISE_FILE="$WORKSPACE_DIR/mise.toml"– Points to themise.tomlfile.if [[ -f "$MISE_FILE" ]]; then– Checks if the file exists./usr/local/bin/mise trust "$MISE_FILE"– Tells mise to trust the config file (no prompt)./usr/local/bin/mise install– Installs all the tools listed inmise.toml.else… – Shows a warning ifmise.tomlis missing.if command -v zsh >/dev/null 2>&1; then– Checks if zsh exists.sudo chsh -s "$(command -v zsh)" "$USER" || true– Sets zsh as the default shell if possible.
Why this script matters:
It prepares your container by installing tools automatically from mise.toml. You only need to run the container; everything else happens by itself.
Step 4: mise.toml Example
[tools]
bat = "latest"
chezmoi = "latest"
fzf = "latest"
lsd = "latest"
ripgrep = "latest"
You can also add programming languages:
node = "20"
python = "3.12"
go = "1.22"
terraform = "1.8.5"
When you run mise install, all tools are downloaded and ready to use.
Step 5: Optional chezmoi Integration
chezmoi manages configuration files on your local system.
You can use it to install mise automatically.
.chezmoiexternals/mise.toml
[".local/bin/mise"]
type = "file"
executable = true
url = "https://mise.jdx.dev/mise-latest-{{.chezmoi.os}}-{{.chezmoi.arch}}"
For macOS:
brew install mise
Step 6: Optional Zsh and Pure Prompt
Install pure prompt:
mkdir -p "$HOME/.zsh"
git clone https://github.com/sindresorhus/pure.git "$HOME/.zsh/pure"
Add this to .zshrc:
autoload -U promptinit; promptinit
prompt pure
eval "$(/usr/local/bin/mise activate zsh)"
This gives you a clean prompt and auto-loads mise every time you open the terminal.
Step 7: Quick Start
- Start the container
devpod up .or in VS Code → Reopen in Container - Check mise
mise --version mise which bat - Add or update tools
mise install
Troubleshooting
mise: command not found– Rebuild the container or open a new shell.- Permission denied for setup script – Run
chmod +x scripts/setup. mise.tomlmissing warning – Add it at the project root.- Wrong home path – Change
/home/vscodein Dockerfile if your username differs.