Publishing Go Binaries the Right Way
Go Releaser
Publishing Go Binaries the Right Way: My Journey Into GoReleaser & GitHub Releases
If you’ve ever built a Go project and wanted to share the binary with others then you’ve probably hit the same questions I did:
- How do I generate binaries for multiple platforms?
- How do people actually publish versioned releases on GitHub?
- Why does every serious project have tags like v0.9.3?
- What are those checksum files and why do package managers rely on them?
Recently, I decided to explore this entire workflow properly by building a Go project and wiring it up with GoReleaser. What I expected to be a quick experiment turned out to be a surprisingly enlightening journey through some essential software-release concepts.
This post is a summary of everything I learned. Explained simply, cleanly, and practically.
Why Releases Matter
On GitHub, a release is more than just a commit. It’s a versioned snapshot of your project that users can download and run. A release usually includes:
- Compiled binaries
- Compressed archives
- Checksums
- A changelog
- Metadata like version and build date
When you run a big open-source tool (Terraform, kubectl, Go itself), you’re actually downloading one of these GitHub Releases.
And the key trigger for a release? A Git tag.
Tags: The Heartbeat of Versioning
A tag marks an official version of your code like v0.1.0, v1.0.0, etc.
You create one with:
git tag -a v0.1.0 -m "First release"
git push origin v0.1.0This does not push your code; that’s what git push origin main is for. Instead, pushing a tag pushes a pointer to a specific commit.
Tools like GoReleaser and GitHub Actions react to these tags and generate releases automatically.
Tags = Versions.
Branches = Work in progress.
Introducing GoReleaser
GoReleaser is a tool that takes your Go project and packages it into polished, production-ready releases. With a single command, it:
- Builds binaries for multiple OS/arch combinations
- Embeds version info using ldflags
- Generates .tar.gz or .zip archives
- Produces a checksum file
- Creates a GitHub Release
- Uploads everything automatically
In other words: GoReleaser does for binaries what Docker does for containers automates the painful parts.
The Only Setup You Really Need
All GoReleaser projects start with one config file: .goreleaser.yaml
This file defines:
- What to build
- Which platforms to target
- What your project is named
- How archives and checksums are generated
- Where to publish the release
Once that’s in place, the real fun begins.
GitHub Tokens: Why GoReleaser Needs One
GoReleaser needs permission to talk to GitHub on your behalf. This is done via a Personal Access Token with:
- Repository access
- Contents: Read and write
Once generated, you export it:
export GITHUB_TOKEN="your_token_here"Now the GoReleaser CLI can authenticate and create releases.
Building and Publishing the Release
The moment of truth:
goreleaser release --cleanHere’s what happens:
- GoReleaser checks that your working tree is clean (no uncommitted files allowed — it enforces discipline)
- It reads the latest Git tag
- Builds binaries for Linux, macOS, Windows
- Produces archives like:
goreleaser-starter_0.1.1_linux_amd64.tar.gz - Generates checksums.txt (used to verify authenticity and avoid tampering)
- Creates a GitHub Release
- Uploads all artifacts
In seconds, your tiny Go program becomes a fully packaged, multi-platform, cryptographically verifiable, downloadable release.
That’s powerful.
Why Checksums Matter
A checksum is a SHA256 fingerprint of a file:
- If a binary is corrupted → checksum won’t match
- If someone tampers with it → checksum won’t match
- Package managers rely on checksums for security
Checksum files give users confidence that the file they downloaded is exactly what you published.
Archives: Packaging Binaries Properly
GitHub releases don’t distribute raw binaries alone. They are usually wrapped in archives (.tar.gz or .zip) because:
- Archives are consistent across OSes
- They preserve permissions
- They allow bundling additional files (README, LICENSE, config)
GoReleaser automates this and applies platform-appropriate formats.
Putting It All Together
By the end of this exercise, I had a complete CI-grade release system:
- Write code
- Commit
- Tag version
- Push tag
- Run GoReleaser
Result: A polished release page with everything a real-world project provides.
This small experiment helped me understand the exact process behind professional Go projects that publish binaries and it gave me a clean starter template I can reuse across future tools.
Final Thoughts
Learning GoReleaser wasn’t just about generating binaries. It taught me the foundational workflow of software distribution:
- Why tags matter
- How GitHub Releases are structured
- What archives and checksums are for
- How version metadata is embedded
- How automation improves consistency
This knowledge scales well beyond small Go projects as it’s the backbone of many open-source and enterprise release pipelines today.