# goenv: per-project Go version management with shims

> goenv is a pyenv-style version manager for Go, cloned from pyenv and rewritten as a Go CLI in v3. It is useful when several projects need different toolchains, and it is more machinery than a single-toolchain machine needs.

**go-nv/goenv** — :blue_car: Like pyenv and rbenv, but for Go.

- Repository: https://github.com/go-nv/goenv
- Website: https://github.com/go-nv/goenv
- Stars: 2,548 · Forks: 262
- Language: Shell
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/go-nv-goenv

## What goenv actually solves for Go teams

Go projects drift apart on toolchain versions. One repository compiles only under an older release, another needs a newer one, and a machine-wide install forces you to pick a winner and reinstall whenever you switch. goenv addresses that by making the Go version a property of the directory you are standing in, not of the machine.

The README lists four capabilities: changing the global Go version per user, per-project versions, overriding the version with an environment variable, and searching commands from multiple versions of Go at a time. That fourth one is the part people underestimate. When each version lives in its own directory and goenv dispatches to the right binary, you can query a toolchain you are not currently using without reinstalling anything.

The intended audience is developers and CI maintainers who juggle more than one Go release. If your work fits inside a single version, goenv adds a dispatch layer between your shell and the compiler for no benefit.

## Shims and version resolution: how the pieces fit

goenv is a reimplementation of the pyenv and rbenv model. The README states plainly that the project was cloned from pyenv and modified for Go, and that it follows the same version management approach. That lineage explains the layout: bin/, libexec/, plugins/, completions/, and a src/ directory that can be compiled when GOENV_NATIVE_EXT is set, as the Makefile shows.

The mechanism is shim-based. Instead of your PATH pointing at a Go installation, it points at a directory of small dispatcher scripts. When you run go, the dispatcher consults the version goenv has resolved for the current directory and forwards the call to the matching toolchain. Resolution walks from the current directory upward, so a project-level setting beats a user-level one, and an environment variable beats both. The README's own description of the override behaviour is exactly this: the Go version can be overridden with an environment variable.

Versions themselves come from a plugin. The repository contains plugins/go-build, and the README notes that new Go versions are added automatically on a daily CRON schedule. That is where the installable list comes from, and it is also why the AWS CodeBuild hint in the README pulls the plugin repository before building.

## Installing goenv and pinning a first project

The README gives two install paths. On macOS or Linux, Homebrew:

```bash
brew install goenv
```

If you prefer to keep the checkout under your home directory, the manual route clones the repository to ~/.goenv:

```bash
git clone https://github.com/go-nv/goenv.git ~/.goenv
```

For pipelines that must stay on the older line, the README documents a versioned formula and a branch clone:

```bash
brew install goenv@2 && brew link goenv@2
git clone -b master https://github.com/go-nv/goenv.git ~/.goenv
```

After either install you need the shell integration that puts the shims on PATH. The repository ships completions/ for shell completion and ENVIRONMENT_VARIABLES.md for the variables that tune behaviour; consult those files for your shell rather than copying a snippet from a blog post. Once the shims are active, a new shell should resolve go through goenv.

The first real task is listing installable versions and pinning one to a directory. The command set is documented in COMMANDS.md; the pattern follows pyenv. If you build in AWS CodeBuild, the README's own hint for buildspec.yml, recommended during the pre_build phase, is a single line:

```yaml
- (cd /root/.goenv/plugins/go-build/../.. && git pull)
```

The README adds a side note to that snippet: unset your golang version in the buildspec and run the installer manually.

## Where goenv gets in the way

Shims cost something. Every go invocation goes through a dispatcher, and any tool that resolves the Go binary by absolute path, or that captures the toolchain path once and caches it, will bypass goenv entirely. That is not a bug in goenv; it is the price of the indirection, and it is the same trade-off pyenv and rbenv users already live with.

The version policy is the second thing to plan around. The README declares v3 the current stable release and describes it as a complete rewrite from shell scripts to a Go-based CLI, with full backward compatibility with v2. v2 stays available as goenv@2 and carries a minimum two-year commitment, with support ending on or before December 31, 2028. The README names AWS CodeBuild, production systems that need extra validation time, and Docker containers as the cases for staying on v2. If your pipeline pins goenv by branch or by formula, that split is a migration you have to schedule, not something that happens on its own.

Finally, goenv manages which Go toolchain runs. It does not build Go from source, and it is not a module proxy or a dependency manager. If your problem is reproducible dependency resolution rather than toolchain selection, goenv is the wrong layer.

## goenv against gvm and the container approach

The README itself draws one comparison: moovweb/gvm is described as a different approach modeled after nvm, and goenv is described as more simplified. It also notes that crsmithdev/goenv depends on Go, which is a meaningful difference for a tool whose job is to provide Go in the first place.

The practical distinction is scope. gvm is built around installing and switching Go versions in the nvm style, a heavier manager with its own conventions. goenv keeps the pyenv surface: shims, a version file per directory, an environment override. If you already know pyenv or rbenv, goenv requires almost no new mental model, which is the point of the design.

The other alternative is not a version manager at all. A container image with the toolchain baked in gives you the same per-project isolation without touching your shell, and it travels to CI unchanged. What it does not give you is the ability to type a command against a second Go version on your laptop without pulling an image. Teams that already build in containers often find goenv redundant; teams that work directly on the host usually do not.

## Maintenance, licensing and upgrade cost

The last push to the repository was on 2026-09-22, the same date as the 3.2.1 release. Two other releases sit close behind it: 3.2.0 on 2026-09-04 and 2.2.46 on 2026-09-10, so both the v3 and v2 lines are receiving releases.

Licensing is MIT, per the LICENSE file and the badge in the README. That is permissive and imposes no copyleft obligation on your own code, but this is not legal advice and the LICENSE file is the authority.

Upgrade cost splits by line. Moving from v2 to v3 is described as backward compatible, with a migration guide in docs/MIGRATION.md, so most users are told they can move without changes. The cost that does not disappear is the shim layer itself: any upgrade that changes how the dispatcher resolves versions can surface in scripts that assumed a particular binary path. The Makefile shows the project's own test suite runs through bats with a fake HTTP server on port 8090 for the go-build plugin, which tells you the install path is the part under test.

## Conclusion

Adopt goenv if you keep several Go projects on different toolchains and want the version selected by directory rather than by hand, and if you can accept shim indirection in exchange. Skip it if one Go version covers your work, or if your build environment already pins the toolchain through a container image. Before rolling it out, verify how your shell's PATH resolves the go shim after the init line, and check whether any existing CI depends on goenv@2 before switching to v3.

## FAQ

### How do I install goenv?

The README gives two routes: brew install goenv on macOS or Linux, or a manual git clone of https://github.com/go-nv/goenv.git into ~/.goenv. Both still require the shell integration that puts the shims on PATH.

### How do I use goenv to switch Go versions?

goenv resolves a Go version per directory, with an environment variable able to override it, and dispatches commands through shims. The exact command set is documented in COMMANDS.md rather than the README.

### What is the difference between goenv v2 and v3?

v3 is the current stable release and is described as a complete rewrite from shell scripts to a Go-based CLI, with full backward compatibility with v2. v2 remains available as goenv@2 for legacy support, with an end of support date on or before December 31, 2028.

### Is goenv the same as Godotenv?

No. goenv is a Go version manager in the pyenv and rbenv tradition, managing which Go toolchain runs. The repository material says nothing about loading .env files, which is what Godotenv does.

### Does goenv work with AWS CodeBuild?

The README includes a CodeBuild hint for buildspec.yml, run during the pre_build phase, that pulls the go-build plugin repository. It also advises unsetting your golang version in the buildspec and running the installer manually.

## Sources

- [go-nv/goenv on GitHub](https://github.com/go-nv/goenv)
- [License: MIT](https://github.com/go-nv/goenv/blob/master/LICENSE)
- [Project website](https://github.com/go-nv/goenv)
- [README](https://github.com/go-nv/goenv/blob/master/README.md)
- [Releases](https://github.com/go-nv/goenv/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/go-nv-goenv
