Go and Nix Integration
This document provides guidance when working with Go projects that use Nix for building and packaging.
When to Use This Document
This integration guide applies when:
- You detect
flake.nix,default.nix, orshell.nixin the project root - The project is in a Nix-managed repository (contains
/pkgsor/toolswith Nix definitions) - The user explicitly mentions Nix or NixOS
Project Structure for Nix-Based Go Projects
Typical Layout in ~/src/home Repository
tools/<tool-name>/
├── cmd/
│ └── <tool-name>/
│ └── main.go # Entry point
├── internal/ # Private application code
│ ├── config/
│ ├── handlers/
│ └── models/
├── pkg/ # Public library code (if needed)
├── go.mod # Module definition
├── go.sum # Dependency checksums
├── README.md
└── *_test.go # Tests alongside code
pkgs/<tool-name>/
└── default.nix # Nix package definition
Multi-Architecture Support
- Build for x86_64-linux and aarch64-linux
- Nix handles cross-compilation automatically
- Custom packages exposed via overlays in
/overlays
Building Go Tools with Nix
Build Commands
# Build a Go tool package
nix build .#<package-name>
# Install to profile
nix profile install .#<package-name>
# Check what would be built
nix build .#<package-name> --dry-run
# Build for specific architecture
nix build .#<package-name> --system aarch64-linux
Development Shell
# Enter dev shell with Go tools available
nix develop
# Run tests in development shell
cd tools/<tool-name>
go test ./...
# Format code (pre-commit hooks use gofmt)
gofmt -w .
Package Definition with buildGoModule
Basic Package Definition
# In pkgs/<package-name>/default.nix
{ lib, buildGoModule }:
buildGoModule {
pname = "tool-name";
version = "1.0.0";
# Source from tools directory
src = ../../tools/tool-name;
# Vendor hash for dependency management
# Set to lib.fakeHash initially, then update with actual hash
vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# Build flags (optional)
ldflags = [
"-s"
"-w"
"-X main.version=${version}"
];
# Metadata
meta = with lib; {
description = "Tool description";
homepage = "https://github.com/user/repo";
license = licenses.mit;
maintainers = [ ];
};
}
Updating Vendor Hash
# Initial build to get vendor hash
nix build .#<package-name> 2>&1 | grep "got:"
# Copy the hash from the error message to vendorHash in default.nix
Common buildGoModule Options
buildGoModule {
# ... other options ...
# Exclude certain packages from building
excludedPackages = [ "cmd/debug-tool" ];
# Skip tests during build
doCheck = false;
# Additional build inputs
nativeBuildInputs = [ pkg-config ];
buildInputs = [ sqlite ];
# Post-install steps
postInstall = ''
installShellCompletion --cmd tool-name \
--bash <($out/bin/tool-name completion bash) \
--zsh <($out/bin/tool-name completion zsh)
'';
# CGO settings
CGO_ENABLED = 0; # Static binary
}
Testing in Nix Environment
Running Tests
# From repository root
nix develop
cd tools/<tool-name>
go test ./...
# With coverage
go test -cover ./...
# Verbose output
go test -v ./...
Pre-commit Hooks
The repository uses pre-commit hooks for Go:
gofmtfor formatting- Can be installed with
make install-hooks
# Install hooks
make install-hooks
# Run pre-commit checks manually
make pre-commit
Integration with Repository Build System
Makefile Targets (if available)
# Build Go tools via Nix
make build
# Run tests
make test
# Format code
make fmt
# Clean build artifacts
make clean
Dependency Management with Nix
Adding Dependencies
# Add Go dependency normally
cd tools/<tool-name>
go get github.com/pkg/errors
go mod tidy
# Update Nix package vendorHash
nix build .#<package-name> # Will show new hash
Updating Dependencies
# Update Go modules
cd tools/<tool-name>
go get -u ./...
go mod tidy
# Update vendorHash in pkgs/<package-name>/default.nix
# Rebuild to verify
nix build .#<package-name>
Common Patterns
Tool with Subcommands
buildGoModule {
pname = "multi-tool";
version = "1.0.0";
src = ../../tools/multi-tool;
# Build only the main command
subPackages = [ "cmd/multi-tool" ];
vendorHash = "sha256-...";
}
Tool with Static Assets
buildGoModule {
pname = "web-tool";
version = "1.0.0";
src = ../../tools/web-tool;
vendorHash = "sha256-...";
# Embed static assets
preBuild = ''
cp -r ${../../tools/web-tool/assets} ./assets
'';
}
Tool Requiring External Dependencies
{ lib, buildGoModule, pkg-config, sqlite }:
buildGoModule {
pname = "db-tool";
version = "1.0.0";
src = ../../tools/db-tool;
nativeBuildInputs = [ pkg-config ];
buildInputs = [ sqlite ];
vendorHash = "sha256-...";
# Enable CGO for database drivers
CGO_ENABLED = 1;
}
Troubleshooting
Vendor Hash Mismatch
# Problem: vendorHash error during build
# Solution: Update hash with the one shown in error message
nix build .#<package-name> 2>&1 | grep "got:"
Missing Dependencies
# Problem: Missing system libraries
# Solution: Add to buildInputs or nativeBuildInputs
buildInputs = [ pkg-config sqlite ];
Test Failures in Nix Build
# Problem: Tests fail during nix build but pass with `go test`
# Solution: Disable tests in package definition
doCheck = false;
# Or fix test dependencies
checkInputs = [ git ];
Best Practices for Nix + Go
- Keep go.mod/go.sum updated: Always run
go mod tidyafter dependency changes - Update vendorHash promptly: Don’t commit package definitions with incorrect hashes
- Test in nix develop: Ensure tools work in Nix environment before building package
- Use proper source paths: Reference tool sources with relative paths (e.g.,
../../tools/name) - Document build requirements: Note any special build flags or dependencies in README
- Leverage overlays: Add custom packages to overlays for repository-wide availability
- Version consistently: Keep version in sync between go.mod and default.nix
Resources
- Nix Manual: https://nixos.org/manual/nix/stable/
- buildGoModule documentation: https://nixos.org/manual/nixpkgs/stable/#sec-language-go
- Repository structure: See
/pkgsand/toolsdirectories for examples