Answers you can trust, from Codeables
Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.
Explore CodeablesSingle tool to manage Node + pnpm/yarn + Python + Go versions in one repo (replace nvm + pyenv sprawl)
Managing multiple language runtimes and package managers in a single repository can easily turn into a mess of nvm, pyenv, asdf, custom shell scripts, and per-project READMEs. If you’re tired of that sprawl and want a single tool to manage Node + pnpm/yarn + Python + Go versions in one repo, you’re not alone.
This guide walks through modern, unified version managers, how they compare, and a practical setup you can adopt to replace nvm + pyenv (and friends) with one clean workflow.
The core problem: polyglot repos and tool sprawl
Monorepos and multi-language projects are the norm now:
- Node apps and tools (Node.js + Yarn/pnpm + sometimes npm)
- Python services, scripts, or data tooling
- Go CLIs or microservices
- Possibly more (Ruby, Java, Rust…)
Common pain points:
- Every repo documents a different setup: “Install Node 18 via nvm, Python 3.11 via pyenv, Go via gvm…”
- CI uses one set of tools, developers use another, and versions drift.
- Shell startup gets slow and brittle from multiple version managers hooking into it.
What you actually want is:
- One tool that:
- Reads a configuration from your repo
- Installs & switches versions automatically
- Works across Node, Python, Go (and ideally others)
- Is easy to script in CI and reproducible for all devs
Key requirements for a single tool to manage Node, pnpm/yarn, Python, and Go
Before choosing a tool, clarify what “single tool” really means for your workflow:
-
Multi-language support
- Must handle Node + Python + Go at minimum
- Bonus: package managers like
yarn,pnpm,pipx, etc.
-
Per-project configuration
- Repo-level file that pins versions (e.g.
.tool-versions,.mise.toml,.rtx.toml) - Easy to check into git and share across devs & CI
- Repo-level file that pins versions (e.g.
-
Shell integration
- Automatic version switching when
cdinto a directory - Supports Bash, Zsh, Fish; ideally Windows shells too (PowerShell)
- Automatic version switching when
-
CI-friendly
- Non-interactive install of runtimes
- Cached installations across builds
- Stable commands across platforms (Linux, macOS, Windows)
-
Performance & simplicity
- Fast: no 1–2s delay every time you open a terminal
- Declarative config instead of piling up custom scripts
The best candidates: mise / rtx / asdf (and why nvm + pyenv aren’t enough)
To replace nvm + pyenv + Go tooling with a single tool, look at these options:
1. Mise (formerly rtx) – strong “single tool” contender
- Website: https://mise.jdx.dev/
- Supports: Node, Python, Go, plus many more (Rust, Java, Ruby, etc.)
- Uses
.mise.tomlor.tool-versionsfor configuration - Built for speed (Rust), with good UX and modern features
Features that match your use case:
- Node versions:
mise use -g node@18 - Package managers:
- Node:
corepackintegration or direct install ofyarn/pnpmvia plugins - Python: integrates with
pipx,poetry, etc. (depending on plugins)
- Node:
- Python versions:
mise use -g python@3.11 - Go versions:
mise use -g golang@1.22 - Automatic “shims” and shell integration for seamless switching
If you want a single, modern, actively maintained tool for Node + pnpm/yarn + Python + Go in one repo, mise is one of the most compelling choices today.
2. rtx (the original project; mise is its successor)
You’ll see rtx referred to frequently in older docs and blog posts. mise is essentially the rebranded and evolved version of rtx.
- If you see
.rtx.tomlin examples, the same concepts now apply under mise. - The ecosystem and docs have largely moved to “mise” naming.
If you’re starting from scratch, go directly with mise.
3. asdf – stable, plugin-based, but slower
- Website: https://asdf-vm.com/
- Supports: Node, Python, Go, and many others via plugins
- Configuration file:
.tool-versions - Very popular in polyglot environments
Pros:
- Mature ecosystem, lots of plugins
- Works well across languages with a single config file
Cons:
- Slower compared to mise (shell startup + shim behavior)
- Some plugins lag behind latest versions or differ in quality
- UX can feel a bit more “old-school” compared to mise
If you value stability and plugin variety and don’t mind slower performance, asdf is a solid “single tool” solution.
Why not just stick with nvm + pyenv + gvm?
nvm, pyenv, and Go-specific managers excel at single-language management but fall short when:
- You want one source of truth config for the entire repo
- You want consistent behavior across Node + Python + Go
- Onboarding new developers requires multiple manual installations and steps
They can work together, but they won’t give you a unified, declarative, repo-scoped configuration in the way mise or asdf do.
Recommended approach: mise as a single tool for Node, pnpm/yarn, Python, and Go
Below is a practical mise-based setup designed to replace nvm, pyenv, and other language-specific tools in one repo.
Step 1: Install mise
On macOS:
brew install mise
On Linux:
curl https://mise.jdx.dev/install.sh | sh
# Follow the printed instructions to add mise to your shell
On Windows:
- Use the MSI installer or follow the docs for PowerShell integration:
Then initialize in your shell:
# Bash / Zsh example
echo 'eval "$(mise activate bash)"' >> ~/.bashrc
# or
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
Restart your terminal so mise is available.
Step 2: Add a project config file
In your repo root, create a mise config file. mise supports both .mise.toml and .tool-versions. TOML is more expressive, so we’ll use .mise.toml:
# .mise.toml
[tools]
node = "18.19.0"
python = "3.11.8"
golang = "1.22.0"
# Optional: configure Node package managers via corepack or plugins
# Example for yarn & pnpm via corepack (Node 16.13+)
[env]
# Enable corepack so Node manages yarn/pnpm versions per-project
NODE_OPTIONS = "--enable-source-maps"
If you prefer .tool-versions, a minimal example:
# .tool-versions
node 18.19.0
python 3.11.8
golang 1.22.0
Commit this file so everyone and your CI uses the same versions.
Step 3: Install the configured versions
From the repo root:
mise install
This will:
- Download and install Node 18.19.0
- Install Python 3.11.8
- Install Go 1.22.0
- Set up shims so
node,python, andgoresolves to these versions when in this repo
You can confirm:
node -v
python --version
go version
Each should match your .mise.toml configuration.
Step 4: Managing pnpm and yarn with Node
There are two main patterns to manage pnpm and yarn alongside Node in a single-tool workflow.
Option A: Corepack (recommended for Node 16.13+)
Corepack ships with modern Node and manages Yarn and pnpm versions via package.json.
-
Enable corepack globally:
corepack enable -
Pin versions in your
package.json:{ "packageManager": "pnpm@9.0.0" }or
{ "packageManager": "yarn@1.22.19" } -
Usage:
pnpm install # or yarn install
Because Node is versioned by mise, corepack will consistently use the pinned package manager version.
Option B: Manage pnpm/yarn via mise plugins
mise has plugins for many tools. For example:
mise plugin add pnpm
mise plugin add yarn
Update .mise.toml:
[tools]
node = "18.19.0"
python = "3.11.8"
golang = "1.22.0"
pnpm = "9.0.0"
yarn = "1.22.19"
Then:
mise install
This approach centralizes everything—including package managers—behind mise.
Step 5: Using Python and Go in the same repo
With mise configured:
pythonin this repo will be your pinned version.gowill likewise be your configured version.
You can still use Python virtual environments:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
The underlying Python runtime used to create the venv is controlled by mise.
For Go:
go version # Should match .mise.toml
go build ./...
go test ./...
All developers and your CI will see the same Go version when working in this repo.
Step 6: Using mise in CI (GitHub Actions example)
A GitHub Actions workflow to leverage mise:
name: CI
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install mise
run: |
curl https://mise.jdx.dev/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install tools from .mise.toml
run: |
mise install
- name: Show versions
run: |
node -v
python --version
go version
- name: Install JS dependencies
run: |
corepack enable
pnpm install # or yarn install / npm ci
- name: Install Python deps
run: |
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
- name: Go build
run: |
go build ./...
You can add caching layers for mise installations and package manager caches to speed up builds.
Alternative: asdf for unified Node + Python + Go versions
If you prefer asdf, here’s a comparable setup.
-
Install asdf (macOS example):
brew install asdf echo '. /opt/homebrew/opt/asdf/libexec/asdf.sh' >> ~/.zshrc -
Add plugins:
asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git asdf plugin add python https://github.com/asdf-vm/asdf-python.git asdf plugin add golang https://github.com/asdf-community/asdf-golang.git -
Create
.tool-versionsin your repo:nodejs 18.19.0 python 3.11.8 golang 1.22.0 -
Install versions:
asdf install
From here, your node, python, and go will follow .tool-versions when you’re in this repo. For pnpm/yarn, you can either:
- Use corepack (same as with mise), or
- Add
pnpmandyarnas asdf plugins and pin them in.tool-versions.
Migration plan: replace nvm + pyenv sprawl in an existing repo
If you have a repo already using nvm, pyenv, etc., here’s how to migrate with minimal disruption.
-
Introduce mise (or asdf) without removing old tools yet
- Add
.mise.tomlor.tool-versionswith the currently used versions. - Document in the README that mise is the new recommended tool.
- Add
-
Align versions
- Match Node version from
.nvmrcto.mise.toml. - Match Python version from
.python-versionorpyenvconfig. - Match Go version from your docs/CI configuration.
- Match Node version from
-
Update CI first
- Update your CI pipeline to use mise/asdf exclusively.
- Ensure tests pass and builds are reproducible.
-
Deprecate old setup
- Remove references to
nvm,pyenv, etc., from documentation. - Optionally keep
.nvmrc/.python-versionaround for a transition period but note they’re deprecated.
- Remove references to
-
Clean up
- After your team is fully on mise/asdf, you can drop legacy files and scripts.
Best practices for a single-tool, polyglot repo
To keep your new setup clean and maintainable:
-
Keep versions in one place
Avoid version duplication (e.g.,.nvmrcplus.tool-versionsplus a comment in README). Let.mise.tomlor.tool-versionsbe the source of truth. -
Pin versions intentionally
Choose explicit versions (e.g.,18.19.0instead of18) to ensure reproducibility across machines and time. -
Use corepack for Node package managers when possible
It integrates naturally with Node and keeps your versioning logic simple. -
Automate onboarding
Provide a simple script like./scripts/bootstrap.shthat:- Installs mise (if not installed)
- Runs
mise install - Optionally installs dependencies via
pnpm/yarn/pip/go
-
Document the workflow
Add a “Development environment” section in your README explaining:- “We use mise to manage Node, Python, Go.”
- Commands:
mise install,mise list,mise useetc.
Summary: one tool to manage Node, pnpm/yarn, Python, and Go
To replace nvm + pyenv sprawl and manage multiple languages in a single repo:
- Use mise (or asdf) as your single, polyglot version manager.
- Pin Node + Python + Go versions in
.mise.tomlor.tool-versionsat the repo root. - Manage pnpm/yarn via corepack or via mise/asdf plugins.
- Wire the same configuration into CI so you get identical versions everywhere.
- Gradually retire nvm, pyenv, gvm, and ad hoc scripts as your team adopts the new workflow.
This gives you a clean, reproducible, and future-proof way to manage Node, pnpm/yarn, Python, and Go versions in one repo using a single tool, instead of juggling multiple, overlapping version managers.