Skip to main content

Managing toolchains

You'll learn:

  • How to pin Node, Python, Rust (and more) per repo.
  • What Versionx's shim layer actually does.
  • How to install, update, and remove runtimes.
  • How to share a set of pins across a fleet.

Prerequisites: Versionx installed; versionx install-shell-hook run once.

Pin a runtime

In versionx.toml:

[runtimes]
node = "22.11.0"
python = "3.13.1"
rust = "1.95"

Versions can be exact (22.11.0), minor-loose (22), semver ranges (^22.11), or latest-in-channel (lts, stable, nightly where meaningful).

Open a new shell inside the repo. node --version resolves to the pinned version via the Versionx shim. When you leave the directory, PATH behavior depends on global defaults (see below).

How shims work

Versionx prepends a single shim directory to your PATH ($XDG_DATA_HOME/versionx/shims on Linux, ~/Library/Application Support/versionx/shims on macOS, %LOCALAPPDATA%\versionx\shims on Windows). Each shim is a tiny native binary that looks up the right version by:

  1. Looking up from the current directory for a versionx.toml.
  2. Falling back to the user's global config.toml.
  3. Dispatching to the real binary in the runtime cache.

Cold-path dispatch is sub-millisecond — the shim uses an mmap'd PATH cache so it doesn't need to parse TOML on every call.

Install what's pinned

versionx sync

Installs every pin in versionx.toml that isn't already cached. Existing installs are not re-downloaded.

Install a specific runtime at a specific version:

versionx install node 22.11.0

List what's installed:

versionx list

Switch versions

Edit versionx.toml. The shim picks up the change immediately — no shell reload needed. versionx install (or versionx sync) will pull the new version if it isn't cached.

You can also override on the command line for one invocation:

versionx run --node 20 -- npm test

Global defaults

Per-user defaults live in $XDG_CONFIG_HOME/versionx/config.toml:

[runtimes]
node = "lts"
python = "3.13"

Used when you're outside any versionx.toml-scoped directory. Repo pins always override globals.

Package manager pinning

Node package managers (pnpm, yarn) and Python package managers (uv, pipx) are pinnable as first-class runtimes:

[runtimes]
node = "22"
pnpm = "9.15.4"
uv = "0.5.8"

Versionx installs them as real binaries — not via corepack (which is being removed in Node 25+).

Clean up

Remove a runtime you no longer use:

versionx uninstall python 3.12.7

Prune runtimes not referenced by any known repo:

versionx prune --dry-run # show what would be removed
versionx prune

Sharing pins across a fleet

For multiple repos that should share a toolchain baseline, put the pins in a fleet config:

# In your ops repo: platform-ops/versionx-fleet.toml
[runtimes]
node = "22"
python = "3.13"

Downstream repos inherit:

# In each repo: versionx.toml
[workspace]
inherit = ["fleet://acme-platform/baseline"]

See Multi-repo & monorepos for how the fleet config is resolved.

Troubleshooting

  • command not found inside a repo. Run versionx doctor. Likely the shim directory isn't on PATH — check your shell rc has the versionx install-shell-hook line.
  • Version mismatch. versionx current prints the authoritative resolution with the reason ("from ./versionx.toml line 4", "from global ~/.config/versionx/config.toml", etc.).
  • Slow first run of a tool. First invocation of a newly-installed runtime pays a shim cache rebuild cost (~5ms). Subsequent runs are sub-ms.

See also