Back to blog

Version Managers Complete Guide: nvm, pyenv, rbenv, jenv & More

devtoolsnodejspythonrubyjavaversion-management
Version Managers Complete Guide: nvm, pyenv, rbenv, jenv & More

Managing multiple versions of programming languages is essential for modern development. Whether you're maintaining legacy projects, testing across versions, or just want the latest features, version managers make this process seamless.

In this comprehensive guide, we'll cover version managers for all major languages: Node.js, Python, Ruby, Java, Go, Rust, and more.


Why Use Version Managers?

The Problem Without Version Managers

Scenario: You have three projects:

  • Project A: Requires Node.js 14 (legacy)
  • Project B: Uses Node.js 18 (stable)
  • Project C: Needs Node.js 20 (bleeding edge)

Without version manager:

# Manually install Node 14
sudo apt install nodejs=14.x
cd project-a && npm install
 
# Uninstall Node 14, install Node 18
sudo apt remove nodejs
sudo apt install nodejs=18.x
cd project-b && npm install
 
# Repeat for Node 20...
# 😱 This is painful!

With version manager (nvm):

nvm install 14 18 20
 
cd project-a && nvm use 14
cd project-b && nvm use 18
cd project-c && nvm use 20
 
# šŸŽ‰ Effortless!

Benefits of Version Managers

āœ… Multiple versions simultaneously - Switch instantly between versions
āœ… Per-project versions - .nvmrc, .python-version files auto-switch
āœ… No sudo required - User-level installations
āœ… Easy cleanup - Remove versions with one command
āœ… Reproducible environments - Same versions across team members
āœ… Testing across versions - Validate compatibility easily


Node.js Version Managers

Option 1: nvm (Node Version Manager)

Most popular choice for Node.js version management.

Installation

macOS/Linux:

# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
 
# Or with wget
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
 
# Add to shell profile (~/.bashrc, ~/.zshrc)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
 
# Reload shell
source ~/.zshrc  # or ~/.bashrc

Windows: Use nvm-windows:

# Download installer from GitHub releases
# Run nvm-setup.exe

Usage

# List available Node versions
nvm ls-remote
 
# Install specific version
nvm install 20.10.0
nvm install 18.19.0
nvm install 14.21.3
 
# Install latest LTS
nvm install --lts
 
# List installed versions
nvm ls
 
# Use specific version
nvm use 20.10.0
 
# Set default version
nvm alias default 20.10.0
 
# Run command with specific version
nvm exec 18.19.0 node app.js
 
# Uninstall version
nvm uninstall 14.21.3

Per-Project Version with .nvmrc

Create .nvmrc in project root:

20.10.0

Then:

cd my-project
nvm use  # Automatically uses version from .nvmrc

Auto-switch with shell hook:

# Add to ~/.zshrc or ~/.bashrc
autoload -U add-zsh-hook
load-nvmrc() {
  if [[ -f .nvmrc && -r .nvmrc ]]; then
    nvm use
  fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc

Pros & Cons

Pros:

  • āœ… Most popular and well-maintained
  • āœ… Simple shell script (no compilation)
  • āœ… Cross-platform (with nvm-windows)
  • āœ… Large community support

Cons:

  • āŒ Slow startup time (shell script overhead)
  • āŒ Not native on Windows

Option 2: fnm (Fast Node Manager)

Rust-based alternative to nvm, much faster.

Installation

# macOS/Linux
curl -fsSL https://fnm.vercel.app/install | bash
 
# macOS with Homebrew
brew install fnm
 
# Windows with Chocolatey
choco install fnm
 
# Add to shell profile
eval "$(fnm env --use-on-cd)"

Usage

# List remote versions
fnm ls-remote
 
# Install version
fnm install 20.10.0
 
# Use version
fnm use 20.10.0
 
# Set default
fnm default 20.10.0
 
# Auto-use .nvmrc or .node-version
fnm use

Pros & Cons

Pros:

  • āœ… 20x faster than nvm
  • āœ… Cross-platform (native Windows support)
  • āœ… Compatible with .nvmrc files
  • āœ… Built-in shell integration

Cons:

  • āŒ Less mature than nvm
  • āŒ Smaller community

Recommendation: Use fnm for speed, nvm for stability.


Python Version Managers

Option 1: pyenv

De facto standard for Python version management.

Installation

macOS:

# Install dependencies
brew install openssl readline sqlite3 xz zlib
 
# Install pyenv
brew install pyenv
 
# Add to shell profile
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init --path)"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
 
source ~/.zshrc

Linux:

# Install dependencies (Ubuntu/Debian)
sudo apt update
sudo apt install -y make build-essential libssl-dev zlib1g-dev \
  libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
  libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \
  libffi-dev liblzma-dev
 
# Install pyenv
curl https://pyenv.run | bash
 
# Add to ~/.bashrc
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"

Windows: Use pyenv-win:

Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"

Usage

# List available Python versions
pyenv install --list
 
# Install specific version
pyenv install 3.12.1
pyenv install 3.11.7
pyenv install 3.10.13
 
# List installed versions
pyenv versions
 
# Set global Python version
pyenv global 3.12.1
 
# Set local (per-directory) version
cd my-project
pyenv local 3.11.7  # Creates .python-version file
 
# Set shell session version
pyenv shell 3.10.13
 
# Uninstall version
pyenv uninstall 3.10.13

Virtual Environments with pyenv-virtualenv

# Install plugin
brew install pyenv-virtualenv  # macOS
 
# Or clone manually
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
 
# Add to shell profile
eval "$(pyenv virtualenv-init -)"
 
# Create virtual environment
pyenv virtualenv 3.12.1 my-project-env
 
# Activate
pyenv activate my-project-env
 
# Deactivate
pyenv deactivate
 
# Auto-activate per directory
cd my-project
pyenv local my-project-env  # Auto-activates when entering directory

Pros & Cons

Pros:

  • āœ… Industry standard for Python
  • āœ… Integrates with virtualenv
  • āœ… .python-version file support
  • āœ… Compiles Python from source (optimized)

Cons:

  • āŒ Requires compilation (slow installs)
  • āŒ Many system dependencies

Option 2: asdf with Python Plugin

Unified version manager for multiple languages.

# Install asdf
brew install asdf  # macOS
# Or git clone https://github.com/asdf-vm/asdf.git ~/.asdf
 
# Add Python plugin
asdf plugin add python
 
# Install Python
asdf install python 3.12.1
 
# Set version
asdf global python 3.12.1
asdf local python 3.11.7

Ruby Version Managers

Option 1: rbenv

Simple and Unix-like Ruby version manager.

Installation

# macOS
brew install rbenv ruby-build
 
# Linux (Ubuntu)
sudo apt install rbenv
 
# Or install via Git
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
 
# Add to shell profile
echo 'eval "$(rbenv init - zsh)"' >> ~/.zshrc
source ~/.zshrc

Usage

# List available Ruby versions
rbenv install --list
 
# Install Ruby
rbenv install 3.3.0
rbenv install 3.2.2
 
# List installed versions
rbenv versions
 
# Set global version
rbenv global 3.3.0
 
# Set local (per-directory) version
rbenv local 3.2.2  # Creates .ruby-version
 
# Rehash shims (after installing gems)
rbenv rehash
 
# Uninstall
rbenv uninstall 3.2.2

Pros & Cons

Pros:

  • āœ… Lightweight and simple
  • āœ… No magic (just shims)
  • āœ… Fast

Cons:

  • āŒ Manual rehashing sometimes needed
  • āŒ No gemset management (use bundler instead)

Option 2: rvm (Ruby Version Manager)

Feature-rich alternative with gemsets.

# Install
\curl -sSL https://get.rvm.io | bash -s stable
 
# Install Ruby
rvm install 3.3.0
 
# Use version
rvm use 3.3.0
 
# Create gemset
rvm gemset create my-project
rvm use 3.3.0@my-project

Recommendation: Use rbenv for simplicity, rvm for gemsets.


Java Version Managers

Option 1: jenv

Java version manager similar to rbenv.

Installation

# macOS
brew install jenv
 
# Add to shell profile
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(jenv init -)"' >> ~/.zshrc
source ~/.zshrc

Usage

# Add Java installations
jenv add /Library/Java/JavaVirtualMachines/jdk-21.jdk/Contents/Home
jenv add /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home
jenv add /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home
 
# List versions
jenv versions
 
# Set global version
jenv global 21
 
# Set local version
jenv local 17  # Creates .java-version
 
# Enable Maven/Gradle integration
jenv enable-plugin maven
jenv enable-plugin gradle

Note: jenv doesn't install Java; it only manages existing installations.


Option 2: SDKMAN!

Comprehensive Java ecosystem manager.

Installation

curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

Usage

# List Java versions
sdk list java
 
# Install Java
sdk install java 21.0.1-tem      # Temurin (AdoptOpenJDK)
sdk install java 17.0.9-amzn     # Amazon Corretto
sdk install java 11.0.21-zulu    # Azul Zulu
 
# Use version
sdk use java 21.0.1-tem
 
# Set default
sdk default java 21.0.1-tem
 
# Install other tools
sdk install gradle
sdk install maven
sdk install kotlin
sdk install scala

Pros & Cons

jenv Pros:

  • āœ… Lightweight
  • āœ… Simple

SDKMAN! Pros:

  • āœ… Installs Java for you
  • āœ… Manages Gradle, Maven, Kotlin, Scala
  • āœ… Multiple JDK vendors

Recommendation: Use SDKMAN! for full ecosystem management.


Go Version Manager: gvm

# Install
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
 
# Install Go version
gvm install go1.21.5
 
# Use version
gvm use go1.21.5
 
# Set default
gvm use go1.21.5 --default

Alternative: Just download binaries from go.dev and update PATH.


Rust Version Manager: rustup

Official Rust toolchain installer.

# Install rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
 
# Install toolchains
rustup toolchain install stable
rustup toolchain install beta
rustup toolchain install nightly
 
# Set default
rustup default stable
 
# Use specific version
rustup override set nightly  # For current directory
 
# Update Rust
rustup update

asdf: Universal Version Manager

One tool to rule them all.

Installation

# macOS
brew install asdf
 
# Linux
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.0
 
# Add to shell profile
echo '. "$HOME/.asdf/asdf.sh"' >> ~/.zshrc
echo '. "$HOME/.asdf/completions/asdf.bash"' >> ~/.zshrc
source ~/.zshrc

Usage

# Add plugins
asdf plugin add nodejs
asdf plugin add python
asdf plugin add ruby
asdf plugin add java
asdf plugin add golang
 
# Install versions
asdf install nodejs 20.10.0
asdf install python 3.12.1
asdf install ruby 3.3.0
 
# Set global versions
asdf global nodejs 20.10.0
asdf global python 3.12.1
 
# Set local versions
asdf local nodejs 18.19.0  # Creates .tool-versions file
 
# List all plugins
asdf plugin list all

.tool-versions File

nodejs 20.10.0
python 3.12.1
ruby 3.3.0

Pros & Cons

Pros:

  • āœ… One tool for all languages
  • āœ… Consistent interface
  • āœ… Single .tool-versions file

Cons:

  • āŒ Plugin quality varies
  • āŒ Less language-specific features than dedicated tools

Comparison Table

LanguageRecommended ToolAlternativeNotes
Node.jsfnmnvmfnm is 20x faster
Pythonpyenvasdfpyenv has better virtualenv support
Rubyrbenvrvmrbenv is simpler, rvm has gemsets
JavaSDKMAN!jenvSDKMAN! installs Java for you
GoOfficial binariesgvmGo's backward compatibility is good
Rustrustup-Official tool, use it
Multi-languageasdf-Best for polyglot projects

Best Practices

1. Use Version Files

Always commit version files to Git:

# .nvmrc (Node.js)
20.10.0
 
# .python-version (Python)
3.12.1
 
# .ruby-version (Ruby)
3.3.0
 
# .tool-versions (asdf)
nodejs 20.10.0
python 3.12.1
ruby 3.3.0

2. Document in README

## Prerequisites
 
- Node.js 20.10.0 (use `nvm install`)
- Python 3.12.1 (use `pyenv install 3.12.1`)

3. CI/CD Integration

GitHub Actions with nvm:

- uses: actions/setup-node@v4
  with:
    node-version-file: '.nvmrc'

GitHub Actions with pyenv:

- uses: actions/setup-python@v5
  with:
    python-version-file: '.python-version'

4. Team Consistency

# Setup script for new team members
#!/bin/bash
nvm install
pyenv install
bundle install
npm install

5. Avoid Global Installs

# āŒ BAD
npm install -g typescript
 
# āœ… GOOD (per-project)
npm install --save-dev typescript
npx tsc

Common Workflows

Workflow 1: Testing Multiple Python Versions

# Install versions
pyenv install 3.12.1 3.11.7 3.10.13
 
# Test script
for version in 3.12.1 3.11.7 3.10.13; do
  pyenv shell $version
  python --version
  python -m pytest
done

Workflow 2: Project Onboarding

# New developer setup
cd project
nvm use          # Reads .nvmrc
pyenv install    # Reads .python-version
npm install
pip install -r requirements.txt

Workflow 3: Upgrade Path

# Current: Node 18 → Target: Node 20
nvm install 20.10.0
nvm use 20.10.0
npm install
npm test  # Verify compatibility
# Update .nvmrc
echo "20.10.0" > .nvmrc

Troubleshooting

Issue 1: "command not found: nvm"

Cause: Shell profile not sourced.

Fix:

# Add to ~/.zshrc or ~/.bashrc
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
 
source ~/.zshrc

Issue 2: pyenv build fails

Cause: Missing system dependencies.

Fix (Ubuntu):

sudo apt install -y make build-essential libssl-dev zlib1g-dev \
  libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
  libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \
  libffi-dev liblzma-dev

Issue 3: Slow shell startup with nvm

Cause: nvm is a shell script.

Fix: Use lazy loading:

# Add to ~/.zshrc
export NVM_DIR="$HOME/.nvm"
nvm() {
  unset -f nvm
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  nvm "$@"
}

Or switch to fnm (much faster).

Issue 4: jenv doesn't change Java version

Cause: JAVA_HOME not exported.

Fix:

jenv enable-plugin export

Summary and Key Takeaways

āœ… Version managers enable seamless multi-version development
āœ… Node.js: Use fnm (fast) or nvm (stable)
āœ… Python: Use pyenv with pyenv-virtualenv
āœ… Ruby: Use rbenv (simple) or rvm (feature-rich)
āœ… Java: Use SDKMAN! for ecosystem management
āœ… Rust: Use rustup (official tool)
āœ… Multi-language: Use asdf for consistency
āœ… Commit version files (.nvmrc, .python-version, .tool-versions)
āœ… Document versions in README for team onboarding
āœ… CI/CD integration with version file support


What's Next?

Now that you can manage multiple runtime versions, explore:

Development Tools:

  • Docker for containerized development environments
  • Dev Containers (VS Code) for reproducible setups
  • Nix for declarative environment management

Related Topics:

  • Package managers (npm, pip, bundler, cargo)
  • Virtual environments and isolation
  • CI/CD best practices

Additional Resources

Official Documentation:

Tools:


Have questions about version managers or need help with setup? Leave a comment below!

Happy version managing! šŸš€

šŸ“¬ Subscribe to Newsletter

Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.

We respect your privacy. Unsubscribe at any time.

šŸ’¬ Comments

Sign in to leave a comment

We'll never post without your permission.