# asdf-golang, managing Go versions the way asdf manages everything else

> asdf-golang is a shell plugin that teaches the asdf version manager how to install, select and shim Go toolchains, with per-shell environment scripts, a version resolution rule for go.mod, and a checksum step that can be skipped when it has to be. It is small, and it assumes you already use asdf.

**asdf-community/asdf-golang** — Go plugin for the asdf version manager [maintainer=@kennyp]

- Repository: https://github.com/asdf-community/asdf-golang
- Website: https://github.com/asdf-vm/asdf
- Stars: 645 · Forks: 101
- Language: Shell
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/asdf-community-asdf-golang

## One plugin in a version manager ecosystem

asdf does not implement language version management itself. It is a version manager with a plugin interface, and each language is handled by a separate repository. asdf-golang is the Go plugin, maintained under the asdf-community organisation, and the repository homepage points back to the main asdf project.

The problem it solves is specific. Go distributes as a single tarball per platform, and the official install instructions have you edit your shell profile, and when you need a second version you end up juggling download directories and PATH entries by hand. With asdf in place, Go becomes one of the tools you declare in a `.tool-versions` file next to Node, Python or Ruby, and the switching happens per directory.

That is the value for a team. The version a project needs is committed next to the project, and a new developer gets the same toolchain by running `asdf install` in the checkout. The plugin is the piece that makes Go behave like the other languages in that setup.

The audience is a developer already inside the asdf workflow. There is nothing here for someone who wants a standalone Go version manager, and the plugin assumes asdf is installed and configured, since the install command is an asdf subcommand.

The implementation language is Shell, and the repository layout shows a conventional asdf plugin: a `bin/` directory with the executable scripts asdf calls, a `lib/` directory for shared functions, `spec/` for the shellspec test suite, `test-fixtures/` for test data, and a `set-env` script per shell at the repository root.

## Installing the plugin and shimming the toolchain

Installation is one asdf subcommand.

```bash
asdf plugin add golang https://github.com/asdf-community/asdf-golang.git
```

There is no second step, no global package manager involved and no binary to compile. The plugin is a set of shell scripts, and asdf clones it and calls into it.

The requirements are listed by platform, and they are minimal but specific. On macOS you need GNU Core Utils, installed with `brew install coreutils`. On Debian-based Linux you need GNU Core Utils and curl, installed with `apt install coreutils` and `apt install curl`. The plugin leans on the GNU flavour of the core utilities, which is why the macOS requirement is spelled out as a separate package rather than assumed from the system.

The issues section of the documentation states the platform assumptions plainly: the plugin assumes Linux, FreeBSD or Mac, and it assumes x86_64, i386, i686, armv6l, armv7l, arm64 and ppc64le. That list is the compatibility contract, and it is narrower than Go's own supported platform set.

The project has no GitHub releases, so there is no tagged version to install. asdf tracks the plugin repository itself, and the last push to this repository was on 2026-06-19. Upgrading the plugin therefore means letting asdf pull the latest commit, not bumping a version number.

## Sourcing the right set-env script for your shell

This is the step that trips people up, and the documentation treats it as a first-class part of the setup rather than a footnote.

The explanation: to ensure the Golang environment variables are correctly set when using the asdf Go plugin, you should source the appropriate set-env script for your shell. The documentation flags this as particularly important if you have customised the asdf data directory using the `ASDF_DATA_DIR` environment variable.

Each shell gets its own file in the repository root, and the sourcing line embeds the data directory lookup so that a relocated asdf still resolves.

```bash
  . ${ASDF_DATA_DIR:-$HOME/.asdf}/plugins/golang/set-env.zsh
```

That is the zsh form, and the bash form is the same path with the `.bash` extension.

```bash
  . ${ASDF_DATA_DIR:-$HOME/.asdf}/plugins/golang/set-env.bash
```

Fish and Nushell need their own syntax, since the shell constructs differ.

```fish
  source (echo $ASDF_DATA_DIR | if test -z $it; echo $HOME/.asdf; else echo $it; end)/plugins/golang/set-env.fish
```

```nu
  source (if ($env.ASDF_DATA_DIR | empty?) { echo $nu.env.HOME/.asdf } { echo $env.ASDF_DATA_DIR })/plugins/golang/set-env.nu
```

The reason the fish line is awkward is the same reason the whole file exists. The default value syntax `${ASDF_DATA_DIR:-$HOME/.asdf}` that works in bash and zsh has no direct equivalent in fish, so the plugin has to fall back on a shell pipeline that tests the variable and prints one path or the other. The Nushell version does the same thing with its own conditional expression.

A plugin that is four directories of shell scripts and a Makefile still needs to work in four shells, and that is where most of the work went.

## Version selection follows .tool-versions, then go.mod

Once installed, the version resolution has two rules and a caveat.

The first rule is the explicit one. When using `.tool-versions` or `.go-version`, the exact version specified in the file will be selected. This is the standard asdf mechanism and it is unambiguous.

The second rule is the interesting one, and it applies when you are inside a Go module. When using `go.mod`, the highest compatible version that is currently installed will be selected. The documentation ties this to the Go modules reference, describing it as the highest minor version with a matching major version, and gives a concrete example: a `go 1.14` directive in a `go.mod` file results in the highest installed `1.minor.patch` being selected, not necessarily `1.14.patch`.

That is a deliberate reading of the Go directive as a minimum rather than a pin, and it means your go.mod does not fully determine which toolchain asdf hands you. If you need an exact patch release, `.tool-versions` is the place to say so.

The caveat concerns the opt-out. Users can explicitly exclude or include `go.mod` and `go.work` by setting `ASDF_GOLANG_MOD_VERSION_ENABLED`. The documentation says it currently defaults to `true`, but that this may change in the future, so it should be explicitly set. A setting that may change its default is worth pinning in your shell profile, and the documentation asks you to do exactly that.

The second rule also only applies to versions that are already installed. The plugin selects among what is on the machine, so the go.mod path never triggers an install by itself.

## Reshims, default packages and a checksum escape hatch

Two operational details are worth knowing before you hit them.

The first is the reshim. After using `go get` or `go install` to install a package, you need to run `asdf reshim golang` to get any new shims. The reason is that asdf works through shims, a small executable per binary on the PATH that looks up the real tool in the currently selected version. A newly installed binary has no shim until you create one, so the command appears to work in one shell and not the next.

The second is the default packages feature. The plugin can automatically install a default set of packages with `go get -u $PACKAGE` right after installing a new Go version. To enable it you provide a `$HOME/.default-golang-pkgs` file that lists one package per line, and the example given looks like this.

```bash
// allows comments
github.com/Dreamacro/clash
github.com/jesseduffield/lazygit
```

A non-default location for that file is possible through the `ASDF_GOLANG_DEFAULT_PACKAGES_FILE` variable. This is a convenience for a machine that is set up repeatedly, where the same two tools should exist in every new toolchain, and it is worth understanding that the install runs `go get -u`, which means the latest version of each listed package rather than a pinned one.

The third detail is the checksum. By default the plugin tries to verify the checksum of each install, but the documentation acknowledges that occasionally that is not possible, pointing at an issue in the plugin's tracker. If you need to skip the checksum for some reason, set `ASDF_GOLANG_SKIP_CHECKSUM`.

That flag should be treated as a last resort. Checksum verification is the only thing standing between a corrupted or substituted download and a toolchain you will compile and run code with, and the plugin reports the verification explicitly, printing that the checksum was verified after the download completes.

## Architecture override for Go versions that predate ARM Macs

One variable exists for a specific historical problem, and the documentation explains it with a terminal transcript, which is more useful than a description would have been.

The `ASDF_GOLANG_OVERWRITE_ARCH` variable can be used to override the architecture that is used for determining which Go build to download. The primary use case, as stated, is when attempting to install an older version of Go for use on an Apple M1 computer, as Go was not being built for ARM at the time.

Without the override, the plugin correctly detects the platform and the CPU, and then fails, because the URL for that combination does not exist.

```bash
> asdf install golang 1.15.8
Platform 'darwin' supported!
URL: https://dl.google.com/go/go1.15.8.darwin-arm64.tar.gz returned status 404
```

With the override set to the older architecture, the same command downloads an amd64 build instead.

```bash
> ASDF_GOLANG_OVERWRITE_ARCH=amd64 asdf install golang 1.15.8
Platform 'darwin' supported!
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  116M  100  116M    0     0  98.6M      0  0:00:01  0:00:01 --:--:-- 98.6M
verifying checksum
/Users/<home>/.asdf/downloads/golang/1.15.8/archive.tar.gz: OK
checksum verified
```

The transcript also shows the checksum step in practice, with the archive path, an OK from the verification, and a final confirmation line. This is the documented path for getting those pre-ARM Go versions onto Apple silicon hardware.

The variable is a per-invocation override, set on the command line, so it does not need to live in your profile permanently. Setting it globally would quietly install amd64 builds for every version you install, including the ones that have native ARM releases.

## Shell script project with a shellspec suite

The repository is small and the tooling reflects that it is shell code with tests.

The top-level entries are `bin/`, `lib/`, `spec/`, `test-fixtures/`, the four `set-env` scripts, a `Makefile`, a `.shellspec` file, a `renovate.json` configuration and a LICENSE file. The `Makefile` is three targets, and they say what the project considers quality.

```bash
SRCFILES = $(shell git ls-files "bin/**" "lib/**" "spec/**" "test-fixtures/*.sh")
SHFMT_BASE_FLAGS = -s -i 2 -ci
```

The `format` target runs shfmt with those flags over the tracked shell files, `format-check` runs the same command in diff mode for CI, and `lint` runs shellcheck with the `-x` flag so it follows sourced files. The file list is derived from `git ls-files` rather than a wildcard, which means only tracked files are checked and a new script that has not been added to the index is silently skipped, a small trap for a contributor.

The `.shellspec` file is the giveaway about the test approach. shellspec is a shell testing framework, and the `spec/` directory is where the behaviour of the plugin is asserted, with `test-fixtures/` holding the sample data those tests run against. For a project whose failure mode is a shell script behaving differently on four shells, that is a proportionate amount of testing, and it is more than most asdf plugins bother with.

The contributing section of the documentation is brief, an invitation to open an issue or a pull request. The licence is MIT.

The other observation about maintenance is the date. The last push to this repository was on 2026-06-19, and there are no GitHub releases, so the plugin is consumed as a moving branch of shell scripts. Renovate handles dependency updates, and the CI workflow badge in the documentation points at a main.yml workflow, so the tests do run on every change.

What the project does not have is any statement of supported Go versions or a deprecation policy. Since the plugin's job is to track a language that ships frequently, and it is a community project rather than a vendor one, that absence is the thing to keep an eye on.

## Conclusion

asdf-golang suits a developer or team that already standardises on asdf and needs several Go versions side by side, since the plugin removes the per-version shell setup that a manual Go install leaves behind. It does not suit someone looking for a Go version manager that works without asdf, and it does not suit a platform outside the documented set of Linux, FreeBSD, macOS and the listed CPU architectures. Verify first that the shims resolve after you source the right set-env script for your shell, that the go.mod version rule picks the toolchain your project expects, and that a fresh install passes checksum verification before you set ASDF_GOLANG_SKIP_CHECKSUM for anything.

## FAQ

### How do I install the asdf Go plugin?

Run asdf plugin add golang https://github.com/asdf-community/asdf-golang.git. The plugin is shell scripts, so there is nothing to compile, and you also need GNU Core Utils on macOS or Linux plus curl on Debian-based Linux.

### Why do I have to source a set-env script for asdf-golang?

The documentation says you should source the appropriate set-env script for your shell to ensure the Go environment variables are correctly set, and that this matters especially if you have customised the asdf data directory with ASDF_DATA_DIR. The repository ships set-env.zsh, set-env.bash, set-env.fish and set-env.nu.

### Which Go version does asdf-golang select from a go.mod file?

The highest compatible version that is currently installed, following the Go modules reference: the highest minor version with a matching major version. A go 1.14 directive therefore results in the highest installed 1.minor.patch, not necessarily 1.14.patch. You can exclude or include go.mod and go.work with ASDF_GOLANG_MOD_VERSION_ENABLED.

### Why is my newly installed Go tool not on the PATH?

asdf works through shims, so after go get or go install you need to run asdf reshim golang to create shims for the new binaries. The same applies to tools listed in the $HOME/.default-golang-pkgs file, which are installed automatically after a new Go version is installed.

### How do I install an old Go version on an Apple silicon Mac?

Set ASDF_GOLANG_OVERWRITE_ARCH=amd64 on the install command, for example ASDF_GOLANG_OVERWRITE_ARCH=amd64 asdf install golang 1.15.8. Go was not built for ARM at the time of those releases, so without the override the download URL returns a 404.

## Sources

- [asdf-community/asdf-golang on GitHub](https://github.com/asdf-community/asdf-golang)
- [Issues](https://github.com/asdf-community/asdf-golang/issues)
- [License: MIT](https://github.com/asdf-community/asdf-golang/blob/master/LICENSE)
- [Project website](https://github.com/asdf-vm/asdf)
- [README](https://github.com/asdf-community/asdf-golang/blob/master/README.md)

---

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