Fast JavaScript toolchain versioning without the friction.
A lightweight JavaScript toolchain manager built for speed and simplicity.
The spiritual successor to Volta, made for developers by developers.
Volta is no longer maintained. If you liked Volta's "pin and forget" model -- where node, pnpm, and yarn just work without manual switching -- Driftr carries that torch forward.
Driftr is a new project. It doesn't have Volta's years of polish or fnm's community size. But it has a clean foundation, an honest design, and an active maintainer who actually uses it. If you're looking for something simple that does the job, give it a try. If it's missing something you need, open an issue -- we're listening.
- Multi-tool -- manages Node.js, pnpm, yarn, and bun from a single CLI
- Shim-based --
node,npm,npx,pnpm,pnpx,yarn, andbunjust work, resolved per-project or globally - Fast -- near-zero overhead via
syscall.Execprocess replacement - Minimal -- 2 external dependencies (cobra + toml), everything else is Go stdlib
- Deterministic -- explicit resolution chain:
DRIFTR_<TOOL>(one shell, viadriftr use) > project config >package.json(driftrkey, thenpackageManagerfield for pnpm/yarn/bun) >.nvmrc/.node-version(node) >package.jsonengines.node(node) > global default - nvm-compatible pins --
.nvmrcand.node-versionare read as they are, including thelts,lts/*andlts/<codename>aliases - Secure -- SHA256 and SHA-512 SRI checksum verification on every download
- Simple -- a handful of commands cover the entire workflow
brew tap StackMade/driftr
brew install driftrAfter installing, run driftr setup — it generates the shims and configures your shell PATH automatically (writing to a universally-sourced rc file such as ~/.zshenv). Restart your shell afterwards. See docs/installation.md for details and why PATH ordering matters on macOS.
curl -fsSL https://raw.githubusercontent.com/StackMade/driftr/main/install.sh | shThis downloads the latest release, verifies its checksum, and configures your PATH automatically. See docs/installation.md for all installation methods.
# In a repo that already pins its tools, install what it asks for
driftr install
# Or install tools by name
driftr install node@22
driftr install pnpm@9
driftr install yarn@1
driftr install bun@1
# Set global defaults
driftr default node@22.22.0
driftr default pnpm@9.15.0
# Pin a project (prompts for .driftr.toml or package.json on first use)
cd my-project
driftr pin node@22.22.0
driftr pin pnpm@9.15.0
# Everything just works
node -v # resolves automatically
pnpm -v # resolves automatically| Command | Description |
|---|---|
driftr install [tool[@version]] |
Download and install a tool version (node, pnpm, yarn, bun); with no argument, installs everything the current project pins; a bare tool name installs the latest; node@lts installs the newest LTS release, node@lts/jod a named LTS line |
driftr uninstall <tool@version> |
Remove an installed tool version |
driftr prune [--dry-run] [-y] [--tool <name>] |
Remove installed versions that neither the global default nor the current project uses |
driftr default <tool@version> |
Set the global default version for a tool |
driftr pin <tool@version> |
Pin a version to the current project (.driftr.toml or package.json) |
driftr use <tool@version> |
Print a shell snippet pinning a version for the current shell: eval "$(driftr use node@24)" |
driftr list [tool] |
List installed versions (defaults to node) |
driftr list --remote [tool] |
Browse available remote versions from nodejs.org, the npm registry, or bun's GitHub releases |
driftr outdated |
Compare the versions in use against the newest releases upstream |
driftr which <tool> |
Show which binary would be executed and why |
driftr run --node <ver> -- <cmd> |
Run a command under a specific Node.js version |
driftr setup |
Initialize Driftr, generate shims, and configure your shell PATH |
driftr cache clean |
Remove all cached downloads to free disk space |
driftr cache dir |
Print the cache directory path |
driftr self-update |
Update Driftr to the latest version |
driftr doctor [--fix] |
Check your Driftr installation for common problems; --fix repairs PATH and shims |
driftr node doctor |
Analyze the project's Node.js / pnpm dependency environment |
driftr node optimize [--install] |
Configure pnpm for shared dependency storage (idempotent) |
driftr node clean [--yes] |
Remove node_modules and prune the shared store (dry-run by default) |
driftr node report |
Report node_modules size and shared pnpm store size |
All commands support -v / --verbose for detailed output including resolver tracing and checksum details.
Output is colorized when writing to a terminal. Color is disabled automatically when output is piped/redirected, or when the NO_COLOR environment variable is set.
Driftr does not replace pnpm/npm/yarn. The driftr node commands configure and
maintain pnpm's shared content-addressable store so dependencies are stored once
and reused across every project on the machine, instead of duplicated in each
node_modules.
driftr node doctor # see current package manager + pnpm store configuration
driftr node optimize # enable corepack, point pnpm at ~/.driftr/stores/pnpm,
# and turn on the global virtual store
driftr node report # compare project node_modules size vs shared store size
driftr node clean # dry-run: show what would be removed/pruned
driftr node clean --yes # remove node_modules, reinstall, prune orphaned packagesoptimize is idempotent — already-correct settings are left untouched — and
requires pnpm (run corepack enable first if it is missing).
driftr list --remote [tool] # Show available versions (default: latest 30)
driftr list --remote --limit 10 # Limit output to 10 versions
driftr list --remote --limit 0 # Show all versions (can be 500+ for node)
driftr list --remote --pre pnpm # Include pre-release versions (npm packages only)Installed versions are marked with ●, the active version with >, and the global default with *. Node.js LTS releases show their codename (e.g. LTS: Jod).
driftr outdated # every managed tool
driftr outdated --tool node # just one
driftr outdated --pre # include npm pre-releases
driftr outdated --exit-code # exit 1 when something is behind (CI)The report shows the version each tool resolves to, the newest release on that
major line, the newest release overall, and for node the newest LTS. A tool
whose release source cannot be reached is reported as unknown and does not
stop the rest of the report.
# zsh
echo 'eval "$(driftr completion zsh)"' >> ~/.zshrc
# bash
echo 'eval "$(driftr completion bash)"' >> ~/.bashrc
# fish
driftr completion fish | sourceflowchart TD
A["$ node app.js"] --> B["shim (bin/)"]
B --> C["resolver"]
C --> C1["1. explicit flag"]
C --> C1b["2. DRIFTR_NODE env var\n(set by driftr use)"]
C --> C2["3. .driftr.toml\n(walks up dirs)"]
C --> C3["4. package.json driftr key\n(walks up dirs)"]
C --> C4["5. .nvmrc (node only)\n(walks up dirs)"]
C --> C5["6. .node-version (node only)\n(walks up dirs)"]
C --> C6["7. package.json engines.node\n(node only, newest installed match)"]
C --> C7["8. global config.toml"]
C --> C3b["4b. package.json packageManager field\n(pnpm/yarn/bun only, walks up dirs)"]
C1 & C1b & C2 & C3 & C3b & C4 & C5 & C6 & C7 --> D["syscall.Exec\nreplaces process with real node"]
Shims in ~/.driftr/bin/ intercept calls to node, npm, npx, pnpm, pnpx, yarn, and bun. The resolver determines the correct version, and syscall.Exec replaces the process with the real binary. Standalone tools (node, pnpm, bun) are exec'd directly. Tools that need Node.js (yarn) are exec'd as node <tool-script>.
| Document | Description |
|---|---|
| Installation | Detailed install guide for macOS and Linux |
| Usage | Full CLI reference with examples |
| Configuration | Global and project config format |
| Architecture | Internal design and module overview |
| Contributing | How to contribute to the project |
~/.driftr/
bin/ shims (node, npm, npx, pnpm, pnpx, yarn, bun)
tools/
node/ installed Node.js versions
pnpm/ installed pnpm versions
yarn/ installed yarn versions
bun/ installed bun versions
config/
config.toml global default settings
cache/ downloaded archives + binaries
| Driftr | nvm | Volta | fnm | mise | |
|---|---|---|---|---|---|
| Language | Go | Shell | Rust | Rust | Rust |
| Mechanism | Shims | Shell function | Shims | PATH via shell hook | PATH via shell hook (shims optional) |
| Cost model | ~5ms per tool call, nothing at shell startup | 200-500ms per shell startup | per tool call | per prompt / directory change | per prompt / directory change |
| Direct dependencies | 2 (cobra, toml) | 0 (shell) | 12 | 27 | 79 |
| Tools managed | node, npm, npx, pnpm, pnpx, yarn, bun | node + its bundled npm | node, npm, yarn; pnpm experimental (VOLTA_FEATURE_PNPM=1) |
node only | node, pnpm, yarn, bun + hundreds of others |
Reads .nvmrc / .node-version |
Yes | Yes | No | Yes | Yes |
Reads engines.node |
Yes (installed versions) | No | No | Yes (default on) | No |
Reads packageManager |
Yes (pnpm, yarn, bun) | No | No | Via corepack | Via corepack |
| Checksum verification | Always: SHA-256 (node, bun), SHA-512 SRI (pnpm, yarn) | Yes, SHA-256 | No | No | Opt-in via mise.lock |
| Auto-install a missing pinned version | Yes, on shim call (prompt, or auto_install = true) |
No | Yes | --install-if-missing |
Yes |
| macOS / Linux | Yes | Yes | Yes | Yes | Yes |
| Windows | No | No | Yes | Yes | Yes |
| Latest release | v1.1.0 (2026-08) | v0.40.7 (2026-08) | v2.0.2 (2024-12) | v1.39.0 (2026-03) | v2026.9.1 (2026-09) |
| Maintained | Yes | Yes | No (upstream says so) | Yes | Yes |
| Self-update | driftr self-update |
nvm install script |
No | Package manager | mise self-update |
Verified against upstream repositories and documentation on 2026-09-07. Dependency counts are
direct dependencies from each project's manifest, not the transitive tree. The Driftr cost figure is
the measured gap between running ~/.driftr/bin/node -v and calling the real node binary on an
Apple Silicon Mac. It is per-call overhead, so you pay it when you run a tool and never when you
open a shell.
When to choose Driftr: You want a shim-based manager for Node.js, pnpm, yarn and bun that behaves the way Volta did: pin a version to a project, and the tools follow. Driftr adds nothing to your shell startup, so the same pins hold in scripts, cron jobs and CI without an activate step. It checks the checksum of every download before unpacking it, ships with two library dependencies, and driftr node can point pnpm at one shared store instead of a separate node_modules copy per project.
When to choose something else: fnm if you need Windows. mise if you want one tool for Node, Python, Ruby and everything else; Volta's own README now sends people there. nvm if it already works for you and shell startup time doesn't bother you.
- macOS or Linux
curlorwget(for the install script)- Internet access (to download releases from nodejs.org, GitHub, and the npm registry)
- Go 1.26+ (only if building from source)
Built by StackMade - a family of small, focused tools shipped as code-first products.
Maintained by Krzysztof Słomka, software architect specializing in fintech, distributed systems and event-driven architectures.
MIT
See docs/contributing.md for guidelines on how to contribute.
