Question
I want to use go get with a private repository hosted on GitLab, but I cannot get it to work correctly.
Here is my first attempt:
go get -v gitlab.com/secmask/awserver-go
This produced the following output:
Fetching https://gitlab.com/secmask/awserver-go?go-get=1
https fetch failed.
Fetching http://gitlab.com/secmask/awserver-go?go-get=1
Parsing meta tags from http://gitlab.com/secmask/awserver-go?go-get=1 (status code 200)
import "gitlab.com/secmask/awserver-go": parse http://gitlab.com/secmask/awserver-go?go-get=1: no go-import meta tags
package gitlab.com/secmask/awserver-go: unrecognized import path "gitlab.com/secmask/awserver-go"
It seems go get cannot access the repository metadata, likely because authentication is required.
My second attempt was to configure Git to replace HTTPS with SSH:
[url "ssh://git@gitlab.com/"]
insteadOf = https://gitlab.com/
After that:
go get -v gitlab.com/secmask/awserver-go
still did not work, but this did:
go get -v gitlab.com/secmask/awserver-go.git
However, that causes the project directory to be created as:
src/gitlab.com/secmask/awserver-go.git
I can rename it manually, but I do not want to do that every time I run go get.
What is the proper way to use go get with a private repository, without ending up with the .git suffix in the package path?
Short Answer
By the end of this page, you will understand why go get behaves differently for private repositories, how Go resolves import paths, why .git sometimes appears to fix the problem, and the proper way to configure private module access using Git, SSH, and GOPRIVATE.
Concept
In Go, go get does not simply clone a repository the way git clone does. It first tries to resolve the import path and figure out where the source code lives.
For public repositories on well-known hosts like GitHub or GitLab, Go can usually detect the repository automatically. For private repositories, that process can fail because:
- the host may require authentication before returning metadata
- the repository path may not expose
go-importmeta tags - Go may try HTTPS first, even if your real access works through SSH
Historically, adding .git to the import path could help because it gave Go a strong hint that the path refers directly to a Git repository. But this changes the package path itself, which is usually not what you want.
For modern Go projects, the proper approach is usually:
- keep the import path clean, without
.git - configure Git authentication separately, often using SSH keys or access tokens
- tell Go which module paths are private using
GOPRIVATE
This matters because private dependencies are common in real applications:
- internal company libraries
- shared API clients
- private SDKs
- reusable utilities across services
If private repository access is configured correctly, Go tooling works smoothly for fetching, building, testing, and updating dependencies.
Mental Model
Think of go get like a delivery service trying to find a building.
- The import path is the street address.
- Go first checks whether that address clearly identifies the building.
- If the address is vague, Go asks the hosting site for directions using metadata.
- If the building is private, the guard at the gate may refuse to answer unless you are authenticated.
Adding .git is like writing extra directions on the address label: “this is definitely a Git repo.” That can help the delivery reach the right place, but now the label itself includes .git, which becomes part of the package path.
The better solution is to:
- keep the address clean
- make sure the delivery service has permission to enter
- tell Go in advance that some addresses are private and should be handled differently
Syntax and Examples
Basic private module setup
In modern Go, private repository access is typically configured outside the source code.
1. Mark the repository as private
go env -w GOPRIVATE=gitlab.com/secmask/*
This tells Go that modules matching this path are private.
2. Configure Git to use SSH instead of HTTPS
git config --global url."ssh://git@gitlab.com/".insteadOf https://gitlab.com/
This makes Git rewrite GitLab HTTPS URLs to SSH URLs.
3. Make sure SSH access works
ssh -T git@gitlab.com
If SSH is configured correctly, GitLab should recognize your account.
4. Fetch the module
go get gitlab.com/secmask/awserver-go
Notice that the import path does not include .git.
Example import
package main
import (
"fmt"
"gitlab.com/secmask/awserver-go"
)
func main {
fmt.Println(awserver.Version)
}
Step by Step Execution
Consider this command:
go get gitlab.com/example/internal-lib
Here is what happens conceptually:
Step 1: Go reads the module path
Go sees:
gitlab.com/example/internal-lib
It treats this as an import or module path, not just a Git URL.
Step 2: Go tries to resolve the repository
Go may try to access metadata at a URL such as:
https://gitlab.com/example/internal-lib?go-get=1
This is how it checks whether the path maps to a repository.
Step 3: Authentication may fail
If the repository is private, GitLab may not return the metadata Go expects unless you are authenticated or the host is recognized in a way Go can use.
Step 4: Git transport is used
Once Go knows where the repository is, Git is used underneath to fetch the source.
If your Git config says:
[url "ssh://git@gitlab.com/"]
insteadOf = https://gitlab.com/
then Git can switch to SSH when cloning or fetching.
Step 5: Go stores the dependency
- In older
GOPATHworkflows, it would appear undersrc/...
Real World Use Cases
Private repository access in Go is commonly used in situations like these:
Internal shared libraries
A company may keep common code in a private module:
- authentication helpers
- logging wrappers
- configuration loaders
- database utilities
Example:
import "gitlab.com/acme/platform/auth"
Private SDKs
A backend team might publish an internal SDK for other teams to call company services.
Examples include:
- billing clients
- messaging service clients
- analytics wrappers
Monorepo or multi-repo organizations
One service may depend on another internal package stored in a separate private repository.
CI/CD environments
Build servers often need to run:
go mod download
go test ./...
This only works when private repository authentication is configured correctly for non-interactive environments.
Real Codebase Usage
In real projects, developers usually combine private module settings with a few standard patterns.
1. GOPRIVATE for internal modules
go env -w GOPRIVATE=gitlab.com/acme/*
This prevents Go from treating those modules like public ones through the public proxy and checksum database.
2. SSH keys or deploy keys
Teams often use:
- personal SSH keys for local development
- deploy keys or bot accounts in CI
- access tokens when SSH is not available
3. Clean module paths
Developers keep imports stable and readable:
import "gitlab.com/acme/payments/client"
not:
import "gitlab.com/acme/payments/client.git"
4. Environment-based configuration
In CI pipelines, a setup step often runs before Go commands:
git config --global url."ssh://git@gitlab.com/".insteadOf https://gitlab.com/
go env -w GOPRIVATE=gitlab.com/acme/*
5. Validation and early failure
Teams usually verify access early:
Common Mistakes
1. Appending .git to the import path unnecessarily
This may appear to fix the problem:
go get gitlab.com/secmask/awserver-go.git
But it changes the module path itself.
Use this only if the repository is actually intended to be imported that way, which is uncommon.
2. Forgetting to configure authentication
Broken approach:
go get gitlab.com/secmask/awserver-go
If SSH keys or tokens are not set up, Go cannot fetch the private repository.
Fix:
- configure SSH keys
- or configure HTTPS credentials
- test access with Git directly first
3. Not setting GOPRIVATE
Without GOPRIVATE, Go may try public module infrastructure for a private dependency.
Fix:
go env -w GOPRIVATE=gitlab.com/secmask/*
4. Confusing repository URLs with import paths
These are not always the same thing:
- Git URL:
ssh://git@gitlab.com/secmask/awserver-go.git - Go import path:
gitlab.com/secmask/awserver-go
Comparisons
| Approach | Works for private repos? | Keeps clean import path? | Notes |
|---|---|---|---|
go get gitlab.com/user/repo | Yes, if properly configured | Yes | Preferred approach |
go get gitlab.com/user/repo.git | Sometimes | No | Avoid unless the module path truly includes .git |
git clone manually | Yes | Not applicable | Useful for testing access, but not how dependencies are normally managed |
| HTTPS with username/token | Yes | Yes | Common in CI or restricted environments |
SSH with insteadOf | Yes |
Cheat Sheet
# Mark GitLab paths as private
go env -w GOPRIVATE=gitlab.com/secmask/*
# Rewrite GitLab HTTPS URLs to SSH
git config --global url."ssh://git@gitlab.com/".insteadOf https://gitlab.com/
# Test SSH access
ssh -T git@gitlab.com
# Fetch private module
go get gitlab.com/secmask/awserver-go
Rules to remember
- Use the normal import path, usually without
.git - Configure authentication in Git or SSH, not in the import path
- Set
GOPRIVATEfor private modules - Test Git access separately if
go getfails - Prefer module mode over old
GOPATHworkflows
Quick diagnostics
# Check private module setting
go env GOPRIVATE
# Check Git rewrite rules
git config --global --get-regexp url
# Test repository access with Git
git ls-remote git@gitlab.com:secmask/awserver-go.git
Key idea
If adding .git makes it work, the real issue is usually repository detection or authentication, not that .git belongs in the import path.
FAQ
Why does go get try ?go-get=1?
Go uses that request to discover repository metadata for an import path. This helps it map the path to the correct version control source.
Should I include .git in a Go import path?
Usually no. Only do that if the module was intentionally defined that way, which is rare.
What does GOPRIVATE do?
It tells Go which module paths are private so it should not use the public proxy or checksum database for them.
Is SSH required for private repositories?
No. You can also use HTTPS with credentials or access tokens. SSH is just a common and convenient choice.
Why does Git work but go get still fail?
Because Go first resolves the import path before Git fetches the code. Authentication or metadata resolution may still be misconfigured.
How do I check whether my private module access is configured correctly?
Start with:
ssh -T git@gitlab.com
git ls-remote git@gitlab.com:secmask/awserver-go.git
go env GOPRIVATE
Does this differ between old Go and modern Go?
Yes. Older workflows used GOPATH and go get more directly for installation. Modern Go uses modules and , which is the recommended approach.
Mini Project
Description
Create a small Go project that depends on a private module hosted on GitLab. The project will verify that your environment is correctly configured for private dependency access and will demonstrate the correct import path, Git setup, and module download flow.
Goal
Set up a Go project that successfully imports and builds against a private GitLab module without using .git in the import path.
Requirements
- Create a new Go module for a sample application.
- Configure private module access for a GitLab namespace using
GOPRIVATE. - Configure Git to use SSH for GitLab URLs.
- Import a package from a private repository using a clean Go import path.
- Build the program successfully.
Keep learning
Related questions
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Check if a Value Exists in a Slice in Go
Learn how to check whether a value exists in a slice in Go, and why Go has no Python-style `in` operator for arrays or slices.
Concatenating Slices in Go with append
Learn how to concatenate two slices in Go using append and the ... operator, with examples, pitfalls, and practical usage.