fnm: a Rust Node.js version manager that reads your .nvmrc
🚀 Fast and simple Node.js version manager, built in Rust
At a glance
- What is it?
- fnm is a single-binary Node.js version manager written in Rust, installed by script, Homebrew, Winget, Scoop, Chocolatey or Cargo. It is a good fit if you want fast shell startup and .nvmrc compatibility, and the wrong tool if you need nvm's long tail of shell hooks.
- Who is it for?
- Adopt fnm if you already keep .nvmrc or .node-version files in your repositories and you want a single binary that starts instantly and needs no Node.js to bootstrap itself. Skip it if your workflow depends on nvm's shell integration beyond what fnm env emits, or if you are on Windows Command Prompt, which the README itself describes as not entirely covered.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 67 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What fnm solves, and who ends up using it
Node.js projects pin a version. The pin usually lives in a file at the repository root, either .nvmrc or .node-version, and the job of a version manager is to read that file and put the right node binary on your PATH before you run anything. fnm does exactly that, and it does it from a single binary rather than from a shell script that has to be sourced on every new terminal. That distinction matters more than it sounds. A sourced manager runs shell code at startup, which is where the noticeable delay in a fresh terminal comes from. fnm instead asks you to evaluate the output of one command in your profile, and the README's install script can append that line for you.
The audience is fairly narrow and fairly clear. If you keep several Node projects on one machine, or you contribute to a project whose maintainers pin a version you do not have installed, fnm removes the manual step of switching. The README states cross-platform support for macOS, Windows and Linux, and the list of installation routes reflects that: Homebrew for macOS and Linux, Winget, Scoop and Chocolatey for Windows, Cargo for all three, and a release binary for anyone who wants neither a package manager nor a compiler. People who bounce between Windows and a Unix shell are the ones who benefit most, because the same command set and the same version files work on both sides.
How fnm env and shell setup actually work
The mechanism is a two-step handshake. First, fnm keeps its own directory of downloaded Node versions. Second, your shell profile evaluates the output of fnm env, which prints the environment changes needed to point PATH and the fnm-specific variables at the currently selected version. Nothing about node itself is patched; the binary you invoke is the one fnm placed on PATH.
The README recommends passing an explicit shell to that command rather than letting fnm infer it. The stated reason is that it is a bit faster and avoids process tree detection. That is a real trade-off rather than a cosmetic one: inference has to walk the process tree to work out which shell is asking, and an explicit flag skips that work entirely. If your shell startup time is the reason you moved off a script-based manager, use the explicit form. The README also points at a configuration document for automatic version switching, which is where the --use-on-cd flag appears in the shell snippets. On-cd behaviour is what makes a repository's .node-version file take effect when you cd into the directory instead of when you open a new terminal.
The version file itself is plain text. The README shows the whole workflow in two lines: run node --version, then redirect that output into .node-version. There is no schema and no lockfile. That is convenient for a single project and slightly awkward for a monorepo, where one file at the root cannot describe several packages with different pins.
Installing fnm on Ubuntu, macOS and Windows
On Linux and macOS the README's first route is a shell script. It requires curl and unzip to be present already, and it works for bash, zsh and fish. Running it will download fnm and, unless you pass --skip-shell, append a loader line to the config file for the shell named in $SHELL.
curl -fsSL https://fnm.vercel.app/install | bashAfter that, open a new shell or source your profile. The command fnm --help should print the command list. If you want to control where the binary lands, the script accepts --install-dir; the README notes the default is $XDG_DATA_HOME/fnm, falling back to $HOME/.local/share/fnm on Linux and $HOME/Library/Application Support/fnm on macOS. A combined invocation looks like this, and the --skip-shell flag is the one to remember if you plan to upgrade later, because it keeps the loader line from being appended twice.
curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir "./.fnm" --skip-shellOn Windows, the README lists three package managers plus a release binary. Winget is the shortest path:
winget install Schniz.fnmScoop and Chocolatey are documented as alternatives, and Cargo works on all three platforms if you already have a Rust toolchain. Whichever route you take, the shell setup step is still yours to perform on Windows: the README says to set up your shell for fnm after installing with Winget, Scoop or Chocolatey. For PowerShell the line goes at the end of your profile file, and the README gives the profile locations for PowerShell 5 and 6+ separately.
fnm env --use-on-cd --shell powershell | Out-String | Invoke-ExpressionThen, inside a project, run node --version and redirect it into a version file. The README's own example uses .node-version, and the feature list states that both .node-version and .nvmrc are supported, so an existing repository does not need to change its pin file to work with fnm.
Where fnm gets thin: Command Prompt, removal and the docs
The README is honest about one gap. Windows Command Prompt is described as supported but not entirely covered, and the workaround is a batch startup script with an FNM_AUTORUN_GUARD variable to stop the auto-run from looping. That guard exists because the batch snippet calls back into cmd.exe through a for /F loop, and without the guard the same startup script would run again. If your team lives in cmd.exe rather than PowerShell, this is the part to evaluate before committing.
Removal is documented and blunt: delete the .fnm folder in your home directory and edit your shell configuration to remove references. There is no uninstall command, and no rollback story for a version switch that goes wrong. If fnm downloads a Node release you did not want, the recovery path is to select another version, not to undo an operation.
The configuration and command documentation live in separate files under docs/, linked from the README rather than inlined. That is normal for a project of this size, but it means the README alone will not tell you which commands exist or which configuration keys are available. A reader who only skims the README will miss automatic version switching entirely, even though the README calls it highly recommended. The command reference is generated: the repository has a generate-command-docs script and a .ci directory, so the docs under docs/ are produced from the binary rather than written by hand.
fnm versus nvm: same files, different runtime
The comparison people reach for is nvm, and the projects share a surface: both read .nvmrc, both install Node releases, both let you select a version per shell. The difference is what runs. nvm is a shell function, so it must be sourced, and the version switching happens inside your interactive shell. fnm is a compiled binary that emits environment changes for the shell to evaluate. That is why the README can describe fnm as a single file with instant startup, and it is also why the shell integration is a smaller surface: fnm hands you a string, and your shell does the rest.
The practical consequence is on Windows. nvm's classic form is a Unix shell script, so Windows users historically ended up on a separate reimplementation. fnm documents first-class Windows routes through Winget, Scoop, Chocolatey and a release binary, plus a manifest file in the repository root. If your team is mixed-platform, that is the argument for fnm over nvm, not raw speed.
The counter-argument is ecosystem depth. nvm has years of shell-specific behaviour baked in, and the README's own shell snippets for fnm are short enough to read in a minute, which cuts both ways: less to configure, less to fall back on when a shell does something unusual. The README's note about preferring an explicit --shell flag is a small admission that inference is not always reliable, and that is exactly the class of problem a script-based manager has already solved for its supported shells.
Maintenance, licence and what upgrades cost
The repository is not archived, and the last push was on 2026-07-24. The most recent release listed is v1.39.0 from 2026-03-06, following v1.38.1 in 2024-11-17 and v1.38.0 in 2024-11-14. That gap between the 1.38 line and 1.39 is worth noting if you depend on a fast release cadence: the project is alive, but releases do not arrive on a schedule you can plan around. The version in Cargo.toml and in package.json both read 1.39.0, so the two manifests are kept in step.
Upgrading depends on how you installed it. Homebrew users run brew upgrade fnm. Everyone else re-runs the install script, and the README specifically recommends passing --skip-shell on that second run to avoid duplicating the loader line in your shell config. That flag is the single most useful thing to know before your first upgrade.
The licence is GPL-3.0, stated in Cargo.toml and as GPLv3 in package.json. fnm is a developer tool you invoke, not a library you link against, but GPL-3.0 is still a copyleft licence and the obligations differ from a permissive one. Whether that matters depends on how your organisation treats build and developer tooling in its dependency policy. That is a question for whoever owns that policy; the repository states the licence and does not offer guidance beyond it.
Editorial conclusion
Adopt fnm if you already keep .nvmrc or .node-version files in your repositories and you want a single binary that starts instantly and needs no Node.js to bootstrap itself. Skip it if your workflow depends on nvm's shell integration beyond what fnm env emits, or if you are on Windows Command Prompt, which the README itself describes as not entirely covered. Before rolling it out across a team, verify two things on your own machines: that fnm env --use-on-cd switches versions correctly in the shell you actually use, and that your project's .node-version resolves to a Node release fnm can download. The GPL-3.0 licence is the other item to settle with whoever reviews your dependency policy.
Frequently asked questions
Should I use fnm or nvm?
Both read .nvmrc and both switch Node versions, so the deciding factor is how they run. nvm is a shell function you source; fnm is a compiled binary whose fnm env output your shell evaluates. If you work across macOS, Linux and Windows, fnm documents installation routes for all three, including Winget and Scoop.
Is fnm a fast Node.js version manager?
The README describes it as built with speed in mind and lists instant startup as a feature, which follows from the single-binary design rather than from a published benchmark. The README also suggests passing an explicit shell to fnm env because it is a bit faster and avoids process tree detection.
How do I install fnm on Ubuntu?
The README's script route covers Linux: ensure curl and unzip are installed, then run curl -fsSL https://fnm.vercel.app/install | bash. Homebrew and Cargo are also documented for Linux, and each of those routes still requires the shell setup step afterwards.
What is Schniz fnm?
It is the fnm project hosted at github.com/Schniz/fnm, a Node.js version manager written in Rust. The README states it works with .node-version and .nvmrc files and supports macOS, Windows and Linux.
What is the best Node.js version manager?
The project documentation does not rank version managers, so there is no defensible answer here. What it does support is a narrower question: if you need Windows support through Winget, Scoop or Chocolatey and you want to keep your existing .nvmrc files, fnm covers that combination.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/schniz-fnm)
Community notes