
How to Use a Private Go Module in Your Own Project
Private Go modules are a powerful way to share code within your organization while keeping your proprietary packages secure and out of public repositories. Unlike public modules hosted on places like GitHub or GitLab, private modules live in your company’s internal repositories or private Git servers, allowing you to modularize your codebase without exposing sensitive business logic. This guide will walk you through setting up private Go modules, configuring your development environment to access them, and implementing best practices for dependency management in enterprise environments.
How Private Go Modules Work
Go’s module system relies on version control systems and proxy servers to fetch dependencies. When you import a public module, Go typically uses the public proxy (proxy.golang.org) to download and cache modules. For private modules, you need to configure Go to bypass the public proxy and fetch directly from your private repositories.
The key components that make private modules work include:
- GOPRIVATE environment variable to specify private module patterns
- GOPROXY configuration to control proxy usage
- GONOSUMDB to skip checksum verification for private modules
- Git credentials and SSH keys for repository access
When you run go get
or go mod tidy
, Go checks these environment variables to determine whether to use the public proxy or fetch directly from your private repository. This mechanism ensures your private code never touches external servers while maintaining the convenience of Go’s module system.
Step-by-Step Setup Guide
Let’s start by creating a private module and then consuming it in another project. I’ll assume you’re using a private Git repository, but the process is similar for other version control systems.
Creating Your Private Module
First, create your private module in a new directory:
mkdir mycompany-utils
cd mycompany-utils
go mod init git.mycompany.com/internal/utils
Create a simple utility function in stringutils.go
:
package utils
import "strings"
// TitleCase converts a string to title case
func TitleCase(s string) string {
return strings.Title(strings.ToLower(s))
}
// Reverse reverses a string
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
Tag and push your module to your private repository:
git add .
git commit -m "Initial commit"
git tag v1.0.0
git push origin main
git push origin v1.0.0
Configuring Environment Variables
Now configure your development environment to handle private modules. Add these environment variables to your shell profile:
export GOPRIVATE="git.mycompany.com/*"
export GOPROXY="direct"
export GONOSUMDB="git.mycompany.com/*"
Alternatively, you can set them globally using go env
:
go env -w GOPRIVATE="git.mycompany.com/*"
go env -w GOPROXY="https://proxy.golang.org,direct"
go env -w GONOSUMDB="git.mycompany.com/*"
Setting Up Git Authentication
For HTTPS repositories, configure Git to use your credentials:
git config --global credential.helper store
git config --global url."https://username:token@git.mycompany.com/".insteadOf "https://git.mycompany.com/"
For SSH-based repositories (recommended for security), ensure your SSH key is added to your Git server and configure Git to use SSH:
git config --global url."ssh://git@git.mycompany.com/".insteadOf "https://git.mycompany.com/"
Using Your Private Module
Create a new project that will consume your private module:
mkdir my-app
cd my-app
go mod init git.mycompany.com/apps/my-app
Create a main.go
file that uses your private module:
package main
import (
"fmt"
"git.mycompany.com/internal/utils"
)
func main() {
text := "hello world"
fmt.Printf("Original: %s\n", text)
fmt.Printf("Title Case: %s\n", utils.TitleCase(text))
fmt.Printf("Reversed: %s\n", utils.Reverse(text))
}
Add your private module as a dependency:
go get git.mycompany.com/internal/utils@v1.0.0
go run main.go
Real-World Examples and Use Cases
Private modules shine in several enterprise scenarios. Here are some practical applications I've implemented in production environments:
Shared Authentication Library
Most companies need consistent authentication across microservices. Here's how you might structure a private auth module:
// git.mycompany.com/internal/auth
package auth
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v4"
)
type TokenService struct {
secretKey []byte
}
func NewTokenService(secret string) *TokenService {
return &TokenService{secretKey: []byte(secret)}
}
func (ts *TokenService) GenerateToken(userID string) (string, error) {
claims := jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(time.Hour * 24).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(ts.secretKey)
}
func (ts *TokenService) ValidateToken(tokenString string) (string, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return ts.secretKey, nil
})
if err != nil || !token.Valid {
return "", errors.New("invalid token")
}
claims := token.Claims.(jwt.MapClaims)
return claims["user_id"].(string), nil
}
Configuration Management Module
Another common use case is sharing configuration structures and validation logic:
// git.mycompany.com/internal/config
package config
import (
"fmt"
"os"
"strconv"
)
type DatabaseConfig struct {
Host string
Port int
Username string
Password string
Database string
}
func LoadDatabaseConfig() (*DatabaseConfig, error) {
config := &DatabaseConfig{
Host: getEnvOrDefault("DB_HOST", "localhost"),
Username: getEnvOrDefault("DB_USER", ""),
Password: getEnvOrDefault("DB_PASS", ""),
Database: getEnvOrDefault("DB_NAME", ""),
}
port, err := strconv.Atoi(getEnvOrDefault("DB_PORT", "5432"))
if err != nil {
return nil, fmt.Errorf("invalid DB_PORT: %v", err)
}
config.Port = port
return config, nil
}
func getEnvOrDefault(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
Comparison with Alternatives
Let's compare different approaches for sharing code in Go projects:
Approach | Security | Versioning | Dependency Management | Setup Complexity | Team Collaboration |
---|---|---|---|---|---|
Private Modules | High | Semantic versioning with Git tags | Native Go modules | Medium | Excellent |
Vendor Directory | Medium | Manual copy/paste | Manual management | Low | Poor |
Git Submodules | Medium | Git commits | External to Go | High | Complex |
Monorepo | High | Single repository | Internal packages | Low | Good |
Copy/Paste | High | None | None | Very Low | Very Poor |
Private modules offer the best balance of security, proper versioning, and team collaboration. While the initial setup requires some configuration, the long-term benefits significantly outweigh the complexity.
Advanced Configuration and Best Practices
Multiple Private Repository Patterns
If your organization uses multiple Git servers or has different access patterns, you can configure multiple private patterns:
go env -w GOPRIVATE="git.mycompany.com/*,gitlab.internal.com/*,github.com/myorg/*"
go env -w GONOSUMDB="git.mycompany.com/*,gitlab.internal.com/*,github.com/myorg/*"
Proxy Configuration for Mixed Environments
In environments where you want to use the public proxy for public modules but direct access for private ones:
go env -w GOPROXY="https://proxy.golang.org,direct"
go env -w GOPRIVATE="*.internal.com,github.com/mycompany/*"
Docker and CI/CD Integration
For containerized applications, you'll need to handle authentication in your Dockerfile:
FROM golang:1.21-alpine AS builder
# Install git and ca-certificates
RUN apk add --no-cache git ca-certificates
# Set Go environment variables
ENV GOPRIVATE="git.mycompany.com/*"
ENV GOPROXY="https://proxy.golang.org,direct"
ENV GONOSUMDB="git.mycompany.com/*"
# Configure git for private repositories
ARG GIT_TOKEN
RUN git config --global url."https://token:${GIT_TOKEN}@git.mycompany.com/".insteadOf "https://git.mycompany.com/"
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .
FROM alpine:latest
RUN apk add --no-cache ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]
Build the Docker image with your Git token:
docker build --build-arg GIT_TOKEN=your_access_token -t my-app .
Common Pitfalls and Troubleshooting
Authentication Issues
The most common problem is authentication failures. If you get permission denied errors:
- Verify your Git credentials work manually:
git clone https://git.mycompany.com/internal/utils.git
- Check that your SSH key is properly configured and added to your Git server
- For HTTPS authentication, ensure your token has appropriate permissions
- Use
go env GOPRIVATE
to verify your environment variables are set correctly
Module Not Found Errors
If Go can't find your private module:
# Clear the module cache
go clean -modcache
# Verify the module path matches exactly
go list -m git.mycompany.com/internal/utils
# Check if the tag exists
git ls-remote --tags https://git.mycompany.com/internal/utils.git
Checksum Mismatch Issues
If you encounter checksum verification errors, ensure GONOSUMDB includes your private domain:
go env -w GONOSUMDB="git.mycompany.com/*"
go clean -modcache
go mod download
Network and Proxy Issues
In corporate environments with strict firewalls:
- Ensure your internal Git server is accessible from your development machine
- Configure corporate proxy settings if needed
- Consider using VPN or bastion hosts for accessing internal repositories
- Test connectivity with tools like
telnet
orcurl
Performance and Security Considerations
Private modules can impact build performance, especially in CI/CD pipelines. Here are some optimization strategies:
Module Proxy Cache
Consider setting up an internal module proxy using Athens or GoProxy to cache private modules and reduce Git server load:
# Example Athens configuration
ATHENS_STORAGE_TYPE=disk
ATHENS_DISK_STORAGE_ROOT=/var/lib/athens
ATHENS_GONOSUM_PATTERNS="git.mycompany.com/*"
Security Best Practices
- Use SSH keys instead of HTTPS tokens when possible for better security
- Rotate access tokens regularly and use fine-grained permissions
- Implement proper branch protection and code review processes
- Consider using semantic versioning strictly to avoid supply chain issues
- Monitor access logs to your private repositories
Version Management Strategy
Establish clear versioning practices for your private modules:
# Use semantic versioning for releases
git tag v1.2.3
# Use pre-release versions for testing
git tag v1.3.0-beta.1
# Use pseudo-versions for development
go get git.mycompany.com/internal/utils@main
Private Go modules provide a robust solution for sharing code within organizations while maintaining security and proper dependency management. The initial setup investment pays dividends in code reusability, consistency across projects, and team productivity. By following these practices and understanding the common pitfalls, you'll be able to implement private modules successfully in your development workflow.
For more detailed information about Go modules, check out the official Go Modules documentation and the Private Modules guide.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.