Dev

Install Go (Golang) on RHEL, Rocky Linux and AlmaLinux 10 / 9 / 8

You can install Go on RHEL with one command, sudo dnf install golang. On the day we tested, Rocky Linux and AlmaLinux 10, 9 and 8 all got the same Go release from it, built from the same RHEL sources. What you also get is two Red Hat defaults that upstream Go doesn’t have: a build that refuses any go.mod asking for a newer Go, and on RHEL 10 binaries that will not start on CPUs without AVX2. Both are easy to change once you know they are there.

Original content from computingforgeeks.com - post 7410

Below you’ll find the dnf package, the official go.dev tarball for the newest release, a first module, go install for tools, and fixes for both defaults with the exact errors they produce. We tested it on fresh Rocky Linux 10.2, 9.8 and 8.10 VMs in October 2026, with Go 1.26.7 from AppStream and Go 1.27.2 from go.dev, and confirmed the dnf package on AlmaLinux 10, 9 and 8.

Pick dnf or the go.dev tarball

Both methods give you a working Go, and they can live side by side, so the choice is about defaults and updates rather than features. The dnf package updates with the rest of the system and follows Red Hat’s patching, while the tarball tracks upstream releases and keeps upstream’s defaults. Neither is wrong, but mixing them without knowing which one a shell runs is how builds start behaving differently on two machines that look identical.

Settinggolang from AppStreamgo.dev tarball
Location/usr/lib/golang, go in /usr/bin/usr/local/go
Version on test daythe same release on RHEL 10, 9 and 8the newest upstream release
Updatesdnf upgradedownload the next tarball
GOTOOLCHAINlocalauto
GOAMD64v3 on RHEL 10, v2 on RHEL 9, v1 on RHEL 8v1

We’d use the dnf package on servers that build internal tools and need security updates without anyone watching Go releases. The tarball is the better fit on developer machines and CI runners, where projects routinely pin a Go version newer than the one Red Hat ships:

  • dnf package for servers that patch through dnf
  • go.dev tarball for development and CI
  • both, if you want the tarball on PATH and the RPM as a fallback

Install Go on RHEL with dnf

Go comes from the AppStream repository as the golang package, on RHEL and on the Rocky Linux and AlmaLinux rebuilds alike, so there is no extra repository to add. On RHEL itself, AppStream is available once the system is registered with subscription-manager. Red Hat rebases Go within a release rather than freezing it, which is why all three releases carried the same version on test day; new upstream releases usually reach AppStream within a few months. Check which version yours offers before installing it:

dnf info golang

Rocky Linux 10 offered Go 1.26.7 from AppStream. The 9 and 8 repositories carried the same version, with release tags 2.el9_8 and a module build on 8:

Name         : golang
Version      : 1.26.7
Release      : 2.el10_2
Repository   : appstream

Install the package. RHEL 8 delivers Go through the go-toolset module, but its default stream enables itself when you install golang, so the command is the same on all three releases. Red Hat’s own documentation installs go-toolset instead, a metapackage that adds the Delve debugger on top of golang; either works for building code.

sudo dnf install -y golang

Expect a larger transaction than the Go package alone, because golang depends on gcc for cgo. On fully updated systems with no compiler yet, the install came to 87 packages on Rocky Linux 10, 17 on Rocky Linux 9 and 29 on Rocky Linux 8. If you already installed the Development Tools group, only the Go packages are added (four on our Rocky Linux 9 and 8 hosts). Confirm the version, and check that it is the Red Hat build you expect.

go version

A Red Hat build names itself in the version string, which is how you tell the RPM apart from an upstream Go at a glance. It matters once a tarball install sits next to it, because go version is the quickest way to see which one a shell is using:

go version go1.26.7 (Red Hat 1.26.7-2.el10_2) linux/amd64

The tarball section later in this guide uses the same check to confirm which Go a shell picks up.

Check the defaults Red Hat sets

Red Hat ships a go.env file inside the Go root that changes two upstream defaults. Print the settings that matter before you build anything, because they decide what happens when a project asks for a newer Go and which CPUs your binaries run on.

go env GOROOT GOPATH GOPROXY GOSUMDB GOTOOLCHAIN

On Rocky Linux 10 the module proxy and checksum database match upstream, while GOTOOLCHAIN does not:

/usr/lib/golang
/home/josphat/go
https://proxy.golang.org,direct
sum.golang.org
local

Those values come from /usr/lib/golang/go.env, and reading the file shows the second change as well.

cat /usr/lib/golang/go.env

On RHEL 10 it sets both GOTOOLCHAIN=local and GOAMD64=v3:

# This file contains the initial defaults for go command configuration.
# Values set by 'go env -w' and written to the user's go/env file override these.
# The environment overrides everything else.

# Use the Go module mirror and checksum database by default.
# See https://proxy.golang.org for details.
GOPROXY=https://proxy.golang.org,direct
GOSUMDB=sum.golang.org

# Automatically download newer toolchains as directed by go.mod files.
# See https://go.dev/doc/toolchain for details.
GOTOOLCHAIN=local


GOAMD64=v3

GOPPC64=power9

Read the comment above GOTOOLCHAIN closely. It still describes upstream’s automatic downloads, but the value under it switches them off. The same file on Rocky Linux 9 sets GOAMD64=v2, and on Rocky Linux 8 it has no GOAMD64 line at all, so Go falls back to v1. Each value matches the CPU baseline its own RHEL release requires, which is why the default is safe on the host that built the binary and a problem when you copy that binary somewhere older. One exception: AlmaLinux also ships an x86_64_v2 build of release 10 for older CPUs, and its golang package has defaulted to v3 there in the past, so check go env GOAMD64 on that variant before you trust it. Next to upstream Go, the RPM’s settings come down to this:

  • GOTOOLCHAIN=local: no automatic toolchain downloads
  • GOAMD64=v3 on RHEL 10 and v2 on RHEL 9: binaries need a newer CPU
  • GOPROXY and GOSUMDB: unchanged from upstream

Build a first Go program

Go projects are modules, so start with a directory and a go.mod. The module path only has to be unique on your machine until you publish the code.

mkdir -p ~/hello && cd ~/hello
go mod init example.com/hello

Go confirms the new module file, which records the module path and the Go version you created it with:

go: creating new go.mod: module example.com/hello

Create the program file in the module directory.

vim main.go

Paste this program. It prints the Go release that compiled it, which is handy once you have both the RPM and the tarball installed:

package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Printf("Hello from %s on %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
}

Run it straight from source, then build a binary.

go run .
go build -o hello . && ./hello

Both print the same line. The binary came to about 2.5 MB on every release:

Hello from go1.26.7 (Red Hat 1.26.7-2.el10_2) on linux/amd64
Hello from go1.26.7 (Red Hat 1.26.7-2.el10_2) on linux/amd64

The RPM’s Go handles this module fine. A project that asks for a newer release is a different story.

go: go.mod requires go >= 1.27.0 (running go 1.26.7; GOTOOLCHAIN=local)

You’ll see this error when a project’s go.mod asks for a newer Go than the RPM provides. We reproduced it with a module that declares go 1.27.0, and the build failed the same way on all three releases:

go: go.mod requires go >= 1.27.0 (running go 1.26.7; GOTOOLCHAIN=local)

Upstream Go would download the requested toolchain and carry on, and local is what stops it. The toolchain is fetched as a module through GOPROXY and verified against GOSUMDB, so the host needs access to proxy.golang.org and sum.golang.org (or your own proxy) for the fix below to work. Set GOTOOLCHAIN=auto for the build to get upstream behaviour back:

GOTOOLCHAIN=auto go build .

Go fetched the requested release through the module proxy and built the project:

go: downloading go1.27.0 (linux/amd64)

To make that permanent for your user, write it to Go’s per-user config with go env -w GOTOOLCHAIN=auto. It lands in ~/.config/go/env and overrides the RPM’s go.env, and go env -u GOTOOLCHAIN puts the Red Hat default back. If your servers must only build with the Go that dnf patched, leave local alone and install a newer Go from go.dev on the machines that need it.

Install the latest Go from go.dev

Use the tarball when you want the newest upstream release with upstream defaults. Read the current version from go.dev, so the download command does not go stale.

GOVER=$(curl -s 'https://go.dev/VERSION?m=text' | head -1)
echo "$GOVER"

On test day go.dev listed Go 1.27.2:

go1.27.2

Download the archive and replace any earlier tarball install. The rm -rf matters: the official install notes warn that untarring into an existing /usr/local/go tree is known to produce broken Go installations. On aarch64 servers, replace linux-amd64 with linux-arm64 in the file name.

curl -sLO "https://go.dev/dl/${GOVER}.linux-amd64.tar.gz"
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf "${GOVER}.linux-amd64.tar.gz"

Put /usr/local/go/bin and ~/go/bin first in PATH for every login shell. Putting the tarball first means it wins over /usr/bin/go if the RPM is also installed:

echo 'export PATH=/usr/local/go/bin:$HOME/go/bin:$PATH' | sudo tee /etc/profile.d/go.sh

Load it in the current shell and check which go runs now.

source /etc/profile.d/go.sh
which go && go version

Now the tarball wins, and a new login shell picked it up the same way without the source:

/usr/local/go/bin/go
go version go1.27.2 linux/amd64

go env GOTOOLCHAIN now prints auto, and the module that failed under the RPM built without any change. The RPM is still there as /usr/bin/go and still updates through dnf, so remove it with sudo dnf remove golang if you do not want two Go installations on the box. Delete the downloaded archive once the install checks out.

Go installed on Rocky Linux 10 from dnf and from the go.dev tarball, showing go version, GOTOOLCHAIN local and GOAMD64 v3

Both installs are in that capture: the dnf build reporting GOTOOLCHAIN=local and GOAMD64=v3, then the tarball’s go version.

Install Go tools with go install

Command-line tools written in Go install into ~/go/bin with go install. If you installed the tarball, that directory is already on your PATH through /etc/profile.d/go.sh. With the dnf package alone it is not, because Rocky Linux only puts ~/.local/bin and ~/bin on PATH, so add it for your user first:

echo 'export PATH=$PATH:$HOME/go/bin' >> ~/.bashrc && source ~/.bashrc

The stringer code generator makes a quick test, because it downloads its dependencies from the module proxy:

go install golang.org/x/tools/cmd/stringer@latest

Go downloaded the tools module and its two dependencies, then placed the binary in ~/go/bin:

go: downloading golang.org/x/tools v0.52.0
go: downloading golang.org/x/sync v0.24.0
go: downloading golang.org/x/mod v0.42.0

Check that the shell finds it without a full path.

stringer -help 2>&1 | head -1

It runs from any directory:

Usage of stringer:

If you get command not found instead, ~/go/bin is not on your PATH in that shell, so open a new login shell or re-run the line above. From here, Git and a container runtime such as Docker CE are the usual next installs for a Go build host. The Fedora Go guide covers the same Red Hat packaging on Fedora, and the Ubuntu 26.04 Go guide covers the Debian side.

Build binaries for older CPUs

RHEL 10’s GOAMD64=v3 default only bites when a binary leaves the machine that built it. A binary built with the RPM on RHEL 10 uses AVX2 and other x86-64-v3 instructions, and Go checks for them at startup. We copied one to a VM whose CPU was limited to x86-64-v2, and it refused to start:

This program can only be run on AMD64 processors with v3 microarchitecture support.

You can see what a binary was built for without running it. go version -m prints the build settings stored inside it:

/usr/bin/go version -m ~/hello/hello | grep GOAMD64

Our RHEL 10 build showed v3, while the same source built with the go.dev tarball showed v1:

	build	GOAMD64=v3

To build for older hardware with the RPM, set GOAMD64 for that build. The full path makes sure the RPM’s go runs even when the tarball is first in PATH. A v1 binary built this way on RHEL 10 ran on the v2-only VM without complaint:

cd ~/hello && GOAMD64=v1 /usr/bin/go build -o hello-v1 .

RHEL 9’s v2 default works the same way, which leaves out the oldest x86-64 machines. Set GOAMD64=v1 in your release builds whenever you don’t control where the binary runs, and keep the Red Hat default for binaries that only ever run on the RHEL release that built them.

Keep reading

Patch PHP2Shell on RHEL, Rocky Linux and AlmaLinux 10 / 9 / 8 AlmaLinux Patch PHP2Shell on RHEL, Rocky Linux and AlmaLinux 10 / 9 / 8 Install GitHub CLI (gh) on Linux, macOS, and Windows Programming Install GitHub CLI (gh) on Linux, macOS, and Windows Customize KDE Plasma Desktop with Themes|Modules|Extensions AlmaLinux Customize KDE Plasma Desktop with Themes|Modules|Extensions Best Laptop Backpacks for Developers and Traveling Sysadmins Programming Best Laptop Backpacks for Developers and Traveling Sysadmins Linux Terminal Shortcuts Cheat Sheet (Bash and zsh) Cheat Sheets Linux Terminal Shortcuts Cheat Sheet (Bash and zsh) Manage SELinux Status, Context, Ports and Booleans Using Ansible CentOS Manage SELinux Status, Context, Ports and Booleans Using Ansible

Leave a Comment

Press ESC to close