How to Install Go and Build CLI Tools on RHEL 7

Go (also known as Golang) is a statically typed, compiled language developed by Google that has become a popular choice for building command-line tools, system utilities, microservices, and cloud-native applications. Its fast compilation times, straightforward toolchain, and ability to produce single, self-contained binaries make it an excellent fit for RHEL 7 production environments. In this tutorial, you will download and install Go from the official tarball, configure your environment variables, initialize a Go module, write a functional CLI tool, and explore advanced topics like cross-compilation and version embedding with ldflags.

Prerequisites

  • A RHEL 7 system with sudo or root access
  • Internet access or a local mirror of Go releases
  • curl or wget installed for downloading the tarball
  • Basic familiarity with the Linux command line and shell scripting

Step 1: Download the Go Tarball

Red Hat Enterprise Linux 7 ships with older versions of Go in its repositories that may not meet the requirements of modern projects. The recommended approach is to download the official tarball directly from go.dev. At the time of writing, Go 1.22 is the current stable release. Adjust the version number as needed.

cd /tmp
curl -LO https://go.dev/dl/go1.22.3.linux-amd64.tar.gz

# Verify the checksum (compare with the hash listed on go.dev/dl)
sha256sum go1.22.3.linux-amd64.tar.gz

Never skip the checksum verification step in production environments. The SHA-256 hash should match exactly what is published on the official Go download page before you proceed.

Step 2: Install Go to /usr/local

The conventional installation location for Go is /usr/local/go. Remove any prior Go installation before extracting the new tarball to avoid mixing versions.

# Remove any existing Go installation
sudo rm -rf /usr/local/go

# Extract the tarball
sudo tar -C /usr/local -xzf /tmp/go1.22.3.linux-amd64.tar.gz

# Confirm the installation
/usr/local/go/bin/go version

You should see output similar to go version go1.22.3 linux/amd64, confirming the binary is functional.

Step 3: Configure PATH and GOPATH

To make go and compiled binaries available system-wide (or for all users), add the necessary environment variables to a profile script. For system-wide configuration, use /etc/profile.d/golang.sh.

sudo tee /etc/profile.d/golang.sh <<'EOF'
# Go installation path
export GOROOT=/usr/local/go

# User workspace for Go modules and installed binaries
export GOPATH=$HOME/go

# Add both bin directories to PATH
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
EOF

# Apply to current shell session
source /etc/profile.d/golang.sh

# Verify
go version
go env GOPATH GOROOT

The GOROOT variable points to the Go installation itself. GOPATH is the workspace where go install places compiled binaries and where module caches are stored. With Go modules (the default since Go 1.16), you no longer need to place your source code inside GOPATH.

Step 4: Initialize a Go Module

Create a new project directory and initialize a Go module. Go modules manage dependencies and replace the older GOPATH-based workflow.

mkdir -p ~/projects/mycli
cd ~/projects/mycli

# Initialize the module with a module path (use your domain in production)
go mod init github.com/youruser/mycli

# Confirm the go.mod file was created
cat go.mod

The go.mod file records the module name and the minimum Go version required. Dependencies you add with go get will also be tracked here along with their cryptographic hashes in go.sum.

Step 5: Write a CLI Tool with the flag Package

The Go standard library includes the flag package for parsing command-line arguments. Create a simple tool that greets users and optionally outputs verbose information.

cat > ~/projects/mycli/main.go <<'EOF'
package main

import (
    "flag"
    "fmt"
    "os"
)

var version = "dev"

func main() {
    name    := flag.String("name", "World", "Name to greet")
    verbose := flag.Bool("verbose", false, "Enable verbose output")
    ver     := flag.Bool("version", false, "Print version and exit")

    flag.Parse()

    if *ver {
        fmt.Printf("mycli version %sn", version)
        os.Exit(0)
    }

    if *verbose {
        fmt.Printf("Running mycli %sn", version)
        fmt.Printf("Arguments: %vn", os.Args[1:])
    }

    fmt.Printf("Hello, %s!n", *name)
}
EOF

Build and test the binary locally:

go build -o mycli .

./mycli --name="RHEL Admin"
./mycli --verbose --name="RHEL Admin"
./mycli --version

Step 6: Add the Cobra CLI Framework

For tools with multiple subcommands (similar to git, kubectl, or docker), the Cobra library is the industry standard. Install it and scaffold a command structure.

cd ~/projects/mycli

# Add cobra as a dependency
go get github.com/spf13/cobra@latest

# Confirm it was added to go.mod
cat go.mod

Refactor main.go to use Cobra with a root command and a subcommand:

cat > ~/projects/mycli/main.go <<'EOF'
package main

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"
)

var version = "dev"

var rootCmd = &cobra.Command{
    Use:   "mycli",
    Short: "A sample CLI tool",
    Long:  "mycli demonstrates Go CLI development on RHEL 7",
}

var greetCmd = &cobra.Command{
    Use:   "greet [name]",
    Short: "Greet a user",
    Args:  cobra.ExactArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Printf("Hello, %s!n", args[0])
    },
}

var versionCmd = &cobra.Command{
    Use:   "version",
    Short: "Print version",
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Printf("mycli %sn", version)
    },
}

func main() {
    rootCmd.AddCommand(greetCmd)
    rootCmd.AddCommand(versionCmd)

    if err := rootCmd.Execute(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}
EOF

go mod tidy
go build -o mycli .

./mycli greet "RHEL Admin"
./mycli version
./mycli --help

Step 7: Cross-Compile for Different OS and Architecture

One of Go’s most powerful features is effortless cross-compilation. You can build a Linux ARM64 binary or a Windows AMD64 executable directly from your RHEL 7 workstation without installing additional toolchains.

# Build for Linux ARM64 (e.g., Raspberry Pi, AWS Graviton)
GOOS=linux GOARCH=arm64 go build -o mycli-linux-arm64 .

# Build for Windows AMD64
GOOS=windows GOARCH=amd64 go build -o mycli-windows-amd64.exe .

# Build for macOS AMD64
GOOS=darwin GOARCH=amd64 go build -o mycli-darwin-amd64 .

# List all supported platforms
go tool dist list

The resulting binaries contain no runtime dependencies — they are fully self-contained and can be copied directly to target systems without installing Go.

Step 8: Embed Version Information with ldflags

Rather than hardcoding version strings, use -ldflags to inject build-time values such as version, commit hash, and build date from your CI pipeline.

# Inject version, commit hash, and build date at compile time
VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "1.0.0")
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILDDATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

go build 
  -ldflags "-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.buildDate=${BUILDDATE} -s -w" 
  -o mycli .

./mycli version

The -s -w flags strip the symbol table and DWARF debugging information, which typically reduces binary size by 20–30%. For production distributions, always include these flags to minimize the footprint of deployed binaries.

Step 9: Install and Distribute as a Single Binary

Go binaries are entirely self-contained. Distribution is as simple as copying a single file. For tools you want available system-wide on RHEL 7, install them to /usr/local/bin.

# Install the locally built binary system-wide
sudo cp mycli /usr/local/bin/mycli
sudo chmod 755 /usr/local/bin/mycli

# Or install directly from a public crates.io-style source using go install
go install github.com/youruser/mycli@latest

# Verify installation
which mycli
mycli --help

For automated deployments, consider packaging the binary in a tarball with a checksum file, or distributing it via an internal Artifactory or Nexus repository. Since Go binaries have no shared library dependencies by default, rollback is as simple as replacing the file.

Conclusion

You have successfully installed Go 1.22 on RHEL 7, configured the necessary environment variables, and built a functional CLI tool using both the standard flag package and the Cobra framework. You explored cross-compilation to produce binaries for multiple platforms without additional toolchains, learned how to embed version metadata at build time with -ldflags, and distributed the result as a single statically linked binary. Go’s simplicity, fast build times, and zero-dependency deployment model make it an outstanding choice for building production-grade system utilities and CLI tools on RHEL 7 environments.