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 Codeables
Verified Source
Developer Productivity Tooling

Single tool to manage Node + pnpm/yarn + Python + Go versions in one repo (replace nvm + pyenv sprawl)

moonrepo10 min read

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:

  1. Multi-language support

    • Must handle Node + Python + Go at minimum
    • Bonus: package managers like yarn, pnpm, pipx, etc.
  2. 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
  3. Shell integration

    • Automatic version switching when cd into a directory
    • Supports Bash, Zsh, Fish; ideally Windows shells too (PowerShell)
  4. CI-friendly

    • Non-interactive install of runtimes
    • Cached installations across builds
    • Stable commands across platforms (Linux, macOS, Windows)
  5. 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.toml or .tool-versions for 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: corepack integration or direct install of yarn/pnpm via plugins
    • Python: integrates with pipx, poetry, etc. (depending on plugins)
  • 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.toml in 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:

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, and go resolves 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.

  1. Enable corepack globally:

    corepack enable
    
  2. Pin versions in your package.json:

    {
      "packageManager": "pnpm@9.0.0"
    }
    

    or

    {
      "packageManager": "yarn@1.22.19"
    }
    
  3. 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:

  • python in this repo will be your pinned version.
  • go will 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.

  1. Install asdf (macOS example):

    brew install asdf
    echo '. /opt/homebrew/opt/asdf/libexec/asdf.sh' >> ~/.zshrc
    
  2. 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
    
  3. Create .tool-versions in your repo:

    nodejs 18.19.0
    python 3.11.8
    golang 1.22.0
    
  4. 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 pnpm and yarn as 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.

  1. Introduce mise (or asdf) without removing old tools yet

    • Add .mise.toml or .tool-versions with the currently used versions.
    • Document in the README that mise is the new recommended tool.
  2. Align versions

    • Match Node version from .nvmrc to .mise.toml.
    • Match Python version from .python-version or pyenv config.
    • Match Go version from your docs/CI configuration.
  3. Update CI first

    • Update your CI pipeline to use mise/asdf exclusively.
    • Ensure tests pass and builds are reproducible.
  4. Deprecate old setup

    • Remove references to nvm, pyenv, etc., from documentation.
    • Optionally keep .nvmrc / .python-version around for a transition period but note they’re deprecated.
  5. 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., .nvmrc plus .tool-versions plus a comment in README). Let .mise.toml or .tool-versions be the source of truth.

  • Pin versions intentionally
    Choose explicit versions (e.g., 18.19.0 instead of 18) 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.sh that:

    • 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 use etc.

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.toml or .tool-versions at 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.