Skip to content

Go Modules Guide

Artifact Keeper provides a Go module proxy compatible with the GOPROXY protocol for hosting Go modules.

Endpoint

Go module operations use the /go/{repo} endpoint:

http://localhost:8080/go/myrepo

This implements the Go module proxy protocol.

Configuration

GOPROXY Environment Variable

Configure Go to use Artifact Keeper as a module proxy:

Terminal window
export GOPROXY="http://localhost:8080/go/myrepo,direct"

For production use with HTTPS:

Terminal window
export GOPROXY="https://registry.example.com/go/myrepo,direct"

The direct fallback allows Go to fetch modules directly from version control if not found in the proxy.

Multiple Proxies

Chain multiple proxies with fallback behavior:

Terminal window
export GOPROXY="https://registry.example.com/go/private,https://proxy.golang.org,direct"

Go will try each proxy in order until one succeeds.

Private Modules

For private modules that should skip checksum verification:

Terminal window
export GOPRIVATE="github.com/yourcompany/*,gitlab.yourcompany.com/*"

This automatically sets both GONOPROXY and GONOSUMDB for the specified patterns.

Alternatively, configure individually:

Terminal window
# Skip proxy for these modules (fetch directly)
export GONOPROXY="github.com/yourcompany/*"
# Skip checksum database verification
export GONOSUMDB="github.com/yourcompany/*"

go env Settings

Make configuration permanent:

Terminal window
go env -w GOPROXY="https://registry.example.com/go/myrepo,direct"
go env -w GOPRIVATE="github.com/yourcompany/*"

View current settings:

Terminal window
go env GOPROXY
go env GOPRIVATE

Reset to defaults:

Terminal window
go env -u GOPROXY
go env -u GOPRIVATE

Downloading Modules

go get

Download and add a module to your project:

Terminal window
go get github.com/yourcompany/mymodule@latest

Get a specific version:

Terminal window
go get github.com/yourcompany/mymodule@v1.2.3
go get github.com/yourcompany/mymodule@v1.2.0

Get a specific commit:

Terminal window
go get github.com/yourcompany/mymodule@abc1234

go mod download

Download modules without adding to go.mod:

Terminal window
go mod download github.com/yourcompany/mymodule@v1.2.3

Download all dependencies:

Terminal window
go mod download

Using Modules

Add to your go.mod:

module github.com/yourcompany/myapp
go 1.21
require (
github.com/yourcompany/mymodule v1.2.3
)

Import in your code:

package main
import (
"github.com/yourcompany/mymodule"
)
func main() {
mymodule.DoSomething()
}

Publishing Modules

Prepare Your Module

Create a go.mod file:

Terminal window
go mod init github.com/yourcompany/mymodule

Example go.mod:

module github.com/yourcompany/mymodule
go 1.21
require (
github.com/sirupsen/logrus v1.9.3
)

Tag a Release

Go modules use Git tags for versioning:

Terminal window
git tag v1.0.0
git push origin v1.0.0

For pre-release versions:

Terminal window
git tag v1.0.0-beta.1
git push origin v1.0.0-beta.1

Upload to Artifact Keeper

Use the HTTP API to publish your module:

Terminal window
# Create a zip archive of your module
git archive --format=zip --prefix=mymodule@v1.0.0/ v1.0.0 -o mymodule-v1.0.0.zip
# Upload to Artifact Keeper
curl -X POST \
-H "Authorization: Bearer your-auth-token" \
-F "file=@mymodule-v1.0.0.zip" \
-F "format=go" \
-F "name=github.com/yourcompany/mymodule" \
-F "version=v1.0.0" \
https://registry.example.com/api/artifacts

Automated Publishing Script

#!/bin/bash
set -e
MODULE_NAME="github.com/yourcompany/mymodule"
VERSION=${1:-$(git describe --tags --abbrev=0)}
REGISTRY="https://registry.example.com"
TOKEN="${ARTIFACT_KEEPER_TOKEN}"
echo "Publishing ${MODULE_NAME}@${VERSION} to ${REGISTRY}"
# Create zip archive
git archive --format=zip --prefix="${MODULE_NAME}@${VERSION}/" "${VERSION}" -o "/tmp/${MODULE_NAME##*/}-${VERSION}.zip"
# Upload to registry
curl -X POST \
-H "Authorization: Bearer ${TOKEN}" \
-F "file=@/tmp/${MODULE_NAME##*/}-${VERSION}.zip" \
-F "format=go" \
-F "name=${MODULE_NAME}" \
-F "version=${VERSION}" \
"${REGISTRY}/api/artifacts"
# Cleanup
rm "/tmp/${MODULE_NAME##*/}-${VERSION}.zip"
echo "Successfully published ${MODULE_NAME}@${VERSION}"

Private Modules Workflow

Setup for Private Modules

Configure GOPRIVATE to bypass public proxies:

Terminal window
# Set for all company modules
export GOPRIVATE="github.com/yourcompany/*,gitlab.yourcompany.com/*"
# Use only your private registry
export GOPROXY="https://registry.example.com/go/private,direct"

Authentication

For authenticated access to private modules:

Terminal window
# Using .netrc for HTTP basic auth
cat >> ~/.netrc <<EOF
machine registry.example.com
login your-username
password your-token
EOF
chmod 600 ~/.netrc

Or use Git credential helpers:

Terminal window
git config --global credential.helper store

Private Module Example

go.mod
module github.com/yourcompany/myapp
go 1.21
require (
github.com/yourcompany/private-lib v1.0.0
github.com/yourcompany/internal-pkg v2.3.1
)
Terminal window
# Download private modules
go mod download
# Build with private dependencies
go build

Version Management

Semantic Versioning

Go modules use semantic versioning:

v1.2.3
│ │ │
│ │ └─ Patch: Bug fixes
│ └─── Minor: New features, backward compatible
└───── Major: Breaking changes

Version Selection

In go.mod:

require (
github.com/yourcompany/mymodule v1.2.3 // Exact version
github.com/yourcompany/other v1.2.0 // Minimum version
)

Update Modules

Update to latest compatible version:

Terminal window
go get -u github.com/yourcompany/mymodule

Update to latest patch release:

Terminal window
go get -u=patch github.com/yourcompany/mymodule

Update all dependencies:

Terminal window
go get -u ./...

List Available Versions

Terminal window
go list -m -versions github.com/yourcompany/mymodule

View Module Information

Terminal window
# Show module details
go list -m github.com/yourcompany/mymodule
# Show all dependencies
go list -m all
# Show dependency graph
go mod graph

Tidy Dependencies

Remove unused dependencies and add missing ones:

Terminal window
go mod tidy

Replace Directive

For local development or forked modules:

go.mod
module github.com/yourcompany/myapp
go 1.21
require (
github.com/yourcompany/mymodule v1.2.3
)
// Use local version for development
replace github.com/yourcompany/mymodule => ../mymodule
// Or use a fork
replace github.com/yourcompany/mymodule => github.com/yourname/mymodule v1.2.4

Remove replace directives for production:

Terminal window
# Temporarily remove replace directives
go mod edit -dropreplace=github.com/yourcompany/mymodule

Integration with CI/CD

GitHub Actions

name: Publish Go Module
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Run tests
run: go test ./...
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Create module archive
run: |
MODULE_NAME=$(go list -m)
git archive --format=zip --prefix="${MODULE_NAME}@${{ steps.version.outputs.VERSION }}/" \
${{ steps.version.outputs.VERSION }} -o module.zip
- name: Publish to Artifact Keeper
env:
ARTIFACT_KEEPER_TOKEN: ${{ secrets.ARTIFACT_KEEPER_TOKEN }}
run: |
MODULE_NAME=$(go list -m)
curl -X POST \
-H "Authorization: Bearer ${ARTIFACT_KEEPER_TOKEN}" \
-F "file=@module.zip" \
-F "format=go" \
-F "name=${MODULE_NAME}" \
-F "version=${{ steps.version.outputs.VERSION }}" \
https://registry.example.com/api/artifacts

GitLab CI

publish:
image: golang:1.21
stage: deploy
script:
- MODULE_NAME=$(go list -m)
- VERSION=${CI_COMMIT_TAG}
- git archive --format=zip --prefix="${MODULE_NAME}@${VERSION}/" ${VERSION} -o module.zip
- |
curl -X POST \
-H "Authorization: Bearer ${ARTIFACT_KEEPER_TOKEN}" \
-F "file=@module.zip" \
-F "format=go" \
-F "name=${MODULE_NAME}" \
-F "version=${VERSION}" \
https://registry.example.com/api/artifacts
only:
- tags

Jenkins Pipeline

pipeline {
agent any
tools {
go 'Go 1.21'
}
environment {
MODULE_NAME = sh(script: 'go list -m', returnStdout: true).trim()
REGISTRY = 'https://registry.example.com'
}
stages {
stage('Test') {
steps {
sh 'go test ./...'
}
}
stage('Publish') {
when {
tag pattern: 'v\\d+\\.\\d+\\.\\d+', comparator: 'REGEXP'
}
steps {
withCredentials([
string(credentialsId: 'artifact-keeper-token', variable: 'TOKEN')
]) {
sh '''
VERSION=${TAG_NAME}
git archive --format=zip --prefix="${MODULE_NAME}@${VERSION}/" ${VERSION} -o module.zip
curl -X POST \
-H "Authorization: Bearer ${TOKEN}" \
-F "file=@module.zip" \
-F "format=go" \
-F "name=${MODULE_NAME}" \
-F "version=${VERSION}" \
${REGISTRY}/api/artifacts
'''
}
}
}
}
}

Docker Build with Private Modules

FROM golang:1.21 AS builder
WORKDIR /app
# Configure Go to use private registry
ENV GOPROXY="https://registry.example.com/go/myrepo,direct"
ENV GOPRIVATE="github.com/yourcompany/*"
# Copy go.mod and go.sum
COPY go.mod go.sum ./
# Download dependencies
RUN --mount=type=secret,id=netrc,target=/root/.netrc \
go mod download
# Copy source code
COPY . .
# Build application
RUN go build -o app .
FROM alpine:latest
COPY --from=builder /app/app /app
ENTRYPOINT ["/app"]

Build with secrets:

Terminal window
docker build --secret id=netrc,src=$HOME/.netrc -t myapp .

Troubleshooting

Authentication Failures

Verify GOPROXY is set correctly:

Terminal window
go env GOPROXY

Test proxy access:

Terminal window
curl https://registry.example.com/go/myrepo/github.com/yourcompany/mymodule/@v/list

Check .netrc permissions:

Terminal window
ls -la ~/.netrc # Should be 600

Module Not Found

Clear module cache:

Terminal window
go clean -modcache

Verify module exists in proxy:

Terminal window
curl https://registry.example.com/go/myrepo/github.com/yourcompany/mymodule/@v/list

Enable verbose output:

Terminal window
go get -v github.com/yourcompany/mymodule@v1.2.3

Checksum Mismatch

For private modules, ensure GOPRIVATE is set:

Terminal window
go env -w GOPRIVATE="github.com/yourcompany/*"

Or disable checksum verification:

Terminal window
export GONOSUMDB="github.com/yourcompany/*"

Clear checksum database:

Terminal window
rm -rf $GOPATH/pkg/sumdb

Version Not Available

List available versions:

Terminal window
go list -m -versions github.com/yourcompany/mymodule

Refresh module cache:

Terminal window
GOPROXY=direct go get github.com/yourcompany/mymodule@v1.2.3

Proxy Timeout

Increase timeout:

Terminal window
export GOPROXY_TIMEOUT=30 # Default is 10 seconds

Check proxy health:

Terminal window
curl -I https://registry.example.com/go/myrepo

Best Practices

Module Structure

Organize your module properly:

mymodule/
├── go.mod
├── go.sum
├── README.md
├── LICENSE
├── pkg/
│ └── public/ # Public API
│ └── api.go
└── internal/ # Internal implementation
└── impl.go

Versioning Strategy

  • Use semantic versioning strictly
  • Tag releases with v prefix: v1.2.3
  • Major version ≥2 requires /vN suffix in module path:
module github.com/yourcompany/mymodule/v2
go 1.21

Documentation

Include comprehensive documentation:

// Package mymodule provides utilities for doing X.
//
// Basic usage:
//
// client := mymodule.New()
// result, err := client.DoSomething()
//
// For advanced usage, see the examples.
package mymodule

Testing Before Publishing

Terminal window
# Run all tests
go test ./...
# Check for race conditions
go test -race ./...
# Verify module integrity
go mod verify
# Check for vulnerabilities
go list -json -m all | go run golang.org/x/vuln/cmd/govulncheck@latest -mode=binary
# Vet code
go vet ./...
# Run linter
golangci-lint run

Minimal Version Selection

Go uses minimal version selection. Specify minimum required versions:

require (
github.com/yourcompany/mymodule v1.2.0 // Requires at least v1.2.0
)

Vendor Dependencies

For reproducible builds:

Terminal window
# Create vendor directory
go mod vendor
# Build using vendor
go build -mod=vendor

Commit vendor directory for air-gapped environments.

Module Proxy Caching

Go caches module downloads. To serve from cache:

~/go/pkg/mod
# Check cache location
go env GOMODCACHE

Retract Versions

Mark versions as retracted in go.mod:

retract (
v1.0.0 // Published accidentally
v1.0.1 // Contains critical bug
)

See Also