Skip to content

0002. mise for Runtime Version Management

Status

Accepted. Amended by ADR 0014 (the mise runtime directory is not volume-mounted, so baked runtimes are never shadowed; no mise install at runtime).

Context

The container needs pinned, reproducible versions of Python and Ruby. Options:

  • System packages (apk add python3 ruby): version tied to Alpine release; no fine-grained control; Renovate cannot track independently.
  • pyenv + rbenv: two separate tools with overlapping shim mechanisms.
  • asdf: plugin-based, mature, but slower and heavier than alternatives.
  • mise (https://mise.jdx.dev/): single binary, manages multiple runtimes, available in Alpine community repo, and Renovate has a native mise manager that can open PRs for version bumps in .mise.toml.

Decision

Use mise to manage Python, Ruby, and Go versions via a .mise.toml file at the repository root.

  • Install mise from the Alpine community repository (apk add mise).
  • Set python_compile = true in .mise.toml settings so mise builds Python from source on musl (precompiled binaries target glibc and segfault on Alpine).
  • Node.js is installed via Alpine system packages (apk add nodejs npm) rather than mise, because it is only needed for Claude Code and does not benefit from per-project version management.
  • Go is managed via mise (go = "1.26", matching monitor/go.mod and the golang builder stage) so the dev-monitor can be built and checked inside the container. Go's official binaries are statically linked, so unlike Python/Ruby it needs no *_compile source-build workaround.

Consequences

Easier: - Single tool replaces pyenv + rbenv. - .mise.toml is the single source of truth for runtime versions. - Renovate automatically opens PRs when new Python/Ruby versions are available. - Adding more runtimes (Go, Java, etc.) later is trivial.

Harder: - On Alpine/musl, Python and Ruby compile from source, adding ~5-10 minutes to the initial Docker build (cached on subsequent builds). - Contributors must understand mise's activation model (eval "$(mise activate bash)").