After reading this document, you should be able to produce Debian packages for Go software.

General notes

Team Maintenance

All Go packages are team-maintained in the pkg-go team. This has multiple advantages:

  1. There are no single points of failure, if one maintainer cannot continue maintaining a package, another maintainer can easily help out or take over.

  2. All packages can share the same technical standards and workflows.

  3. When working within a team anyway, it is much easier to get help on any technical issue.

A package maintained within the team should have the name of the team either in the Maintainer field or in the Uploaders field. We use tracker.debian.org’s team functionality and spell the team like this: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>. This enables the team to have an overview of its packages on the DDPO website and ensures that all packages maintained by the team are automatically added to the Debian Package Tracker team.

Putting the team in Maintainers is a strong statement that fully collaborative maintenance is preferred. Anyone can commit to the git repository and upload as needed. A courtesy email to Uploaders can be nice but not required.

If for some reason, a package needs tighter control by its original maintainers, putting the team in Uploaders is a weak statement of collaboration. Help in maintaining the package is appreciated, commits to the git repository are freely welcomed, but before uploading, please contact the Maintainer for the green light.

Packaging in git

All Go packages are maintained in git and must be buildable gbp build-package using git-buildpackage.

We use Salsa to store the git repositories.

Git setup for automatically authenticating

When using git-buildpackage’s vcsgit feature (e.g. gbp clone vcsgit:golang-text), the following configuration results in git automatically rewriting URLs to be authenticated:

git config --global url."git.debian.org:/git/".insteadOf "https://anonscm.debian.org/git/"
git config --global url."git@salsa.debian.org:".insteadOf "https://salsa.debian.org/"

Using dh-make-golang

Instead of copying the debian/ directory from some random other package, please use dh-make-golang when you start packaging a new Go library/program.

Also dh-make-golang can create Salsa projects, and configure the CI automatically. Guest accounts on Salsa can’t create projects in go-team group, they can only do it with this approach. Please run dh-make-golang create-salsa-project to see the usage.

Version numbers

Many Go libraries (and also some actual Go programs) don’t have version numbers in the traditional sense, but live in a version control repository of some kind.

In case your upstream does not use version numbers, the Debian package version will look like this:

0.0~git20130606.b00ec39-1

  • The 0.0 in the beginning is used to allow upstream to adopt version numbers at any point in time and also to make it clear that no traditional version number is used for this package.

  • The second part is the version control system, e.g. git, hg, svn.

  • Afterwards, a date follows in the format YYYYMMDD.

  • After the dot, the version control system revision follows, to make it clear which commit was packaged, as many repositories have multiple commits on a given day.

  • The last part after the dash is the Debian version number.

In case you make more than one snapshot per day, you can append a snapshot number after the date, e.g. 0.0~git20130606.2.b00ec39-1. This should rarely be necessary.

changelog: UNRELEASED

During the time when you still work on a package, i.e. before it is ready to upload, please put UNRELEASED into the distribution field in debian/changelog (dch -v <debian_version> will do it automatically). When the package is ready for uploading, change it to unstable (dch -r).

If you change something that has to be noted in debian/changelog, just add a line to the current entry (dch -a). The [firstname lastname] markers added by dch are okay to give credit to non-upload-permitted contributors (also for the initial changelog entry).

Important NOTES to other group members may be placed at the top of the current changelog entry of packages that are not yet ready for upload (e.g. why a package is still UNRELEASED, etc.).

Binary-only packages

A binary-only package is a package that contains a program written in Go, but no source code. An example is docker, which is written in Go, but does not offer an API (thus no source code).

Naming Conventions

The source package should be named like the upstream project, i.e. docker, you do NOT need to call it golang-docker.

Similarly, the resulting binary package(s) should NOT contain the golang- prefix.

Use dh-golang

Install dh-golang from Debian unstable so that you are using the newest version. The buildds are using the unstable version, too, so this is important.

You will need to change the value of XS-Go-Import-Path in debian/control to correspond to your program’s upstream package name. This is usually what you would go get when installing it manually. dh-golang needs that information so that it can run go install.

dh-golang sets up a build environment that contains all the libraries that are available in /usr/share/gocode/src, so you need to add Build-Depends to your package. As an example, Debian Code Search depends on golang-github-lib-pq-dev and others: https://github.com/Debian/dcs/blob/master/debian/control#L5

Library (or binary + library) packages

Libraries written in Go are packaged for Debian with the only purpose of building other Go programs for Debian. They are specifically not available for users in their regular development workflow. For that, users should use go get. The rationale behind this decision is:

  • By using go get you are forced to set up the environment variable $GOPATH which you need for other go commands (e.g. go build, go test, etc.).

  • Debian packages install files to system locations, which users cannot modify. That means that users would not be able to upgrade packages with the canonical go get -u <package>. Even worse, when using sudo to forcibly modify the system files, it still would not work since no VCS information is contained in the Debian packages.

The following subsections list some packaging best practices. For discussion, see the debian-go mailing list thread starting at https://lists.debian.org/debian-go/2025/10/msg00013.html.

One Binary Package Per Debian Source Package

A Debian source package that packages up one or more Go library modules should produce a single binary package.

Rationale
  • The XS-Go-Import-Path Field in debian/control describes a property of the Debian source package, not of a binary (built) package. If one Debian source package produces multiple binary packages then dh-make-golang is unable to determine which binary package is associated with a module path, causing dependency bugs.

    Note
    TODO: Adding some heuristics to dh-make-golang to correlate binary package name with import path would reduce the need for this best practice.
    Note
    TODO: Migrating from the per-source XS-Go-Import-Path Field to a per-binary XB-Go-Module-Paths field would eliminate the need for this best practice.

One Go Library Module Per Debian Source Package

Unless it would present a significant maintenance burden, a Debian source package should not contain multiple Go modules. Separate source packages are encouraged even if another Go library module lives in the same upstream source code repository (or orig tarball).

For example, golang.org/x/oauth2 and golang.org/x/oauth2/google are two separate Go modules so they should be packaged by separate Debian source packages despite both living in the https://cs.opensource.google/go/x/oauth2 repository.

If the maintainer does decide to package multiple Go modules in a single source package, each module should be placed in its own binary package (but see One Binary Package Per Debian Source Package).

Rationale
  • dh-golang only uses the first module listed in the XS-Go-Import-Path Field

  • An issue with one of the modules could block a needed upgrade of another module.

  • If multiple modules are put in a single binary package, dependents must be able to accommodate the bound module versions. (Usually dependents want the same versions of related modules so this is not likely to be a problem in practice.)

  • Each Go module might have a different version number. (If not now, then possibly in the future.) If each module is in its own binary package, the binaries could technically be given versions that differ from the source package’s debian/changelog version, but doing so is a maintenance hassle as well as a burden on users reading debian/changelog. One of the binary packages could be split off into its own separate source package if the versions did start to diverge.

  • One of the modules could be moved to a separate repository in the future without changing its module path.

Go Packages That Are Not Inside A Go Module

It is possible to publish a Go package or collection of Go packages without putting them inside a Go module. Few upstream projects publish this way, but you may encounter it with old Go packages that predate Go’s module support. (Go module support was added in Go v1.11 and declared production-ready in Go v1.15.)

For such Go packages, the entire upstream repository (or orig tarball) should be treated as if it was a single Go module whose module path references the repository root (to match what the Go command synthesizes and what the Go module proxy serves). No major version suffix (/v2, etc.) is implicitly added to this pseudo-module path even if the repository is tagged with a v2 or higher revision.

In the rare case that an upstream repository has a mix of Go modules and moduleless Go packages, the Go modules are ignored when packaging the repository. (This is no different from the case where a module is published in a subdirectory of another module.)

Naming Conventions

The source and binary Debian package names are derived from the module’s module path. The module path is the import path prefix for all of the packages in the module, and can be found in the module directive in go.mod. The names are derived as follows:

  • Source package name:

    1. Replace the first component of the module path with its shortened version (e.g., github.com becomes github). For the full shortening rules, see the github.com/Debian/dh-make-golang.shortHostName function.

    2. Replace each slash (/) character with a hyphen (-) character.

    3. Add golang- to the beginning.

  • Binary (built) library package name:

    1. Compute the source package name.

    2. Add -dev to the end.

    We use the -dev suffix to keep the namespace clean for shipping shared libraries in the future.

Important
Use the module path declared in the module directive, not the upstream repository URL, when deriving the Debian package names.

Examples:

Go module path Debian source package name Debian binary library package name

github.com/go-jose/go-jose

golang-github-go-jose-go-jose

golang-github-go-jose-go-jose-dev

github.com/go-jose/go-jose/v4

golang-github-go-jose-go-jose-v4

golang-github-go-jose-go-jose-v4-dev

gopkg.in/yaml.v3

golang-gopkg-yaml.v3

golang-gopkg-yaml.v3-dev

Rationale
  • The major version suffix (/v2, etc.) is included in the source package name (as -v2, etc.) to make it easier to simultaneously install multiple major versions without violating the One Go Library Module Per Debian Source Package best practice, for consistency, and for tooling implementation simplicity.

  • The major version suffix (/v2, etc.) is included in the binary library package name (as -v2, etc.) because two different major versions of a library should be installable at the same time. From Go’s perspective, two Go packages with different import paths, such as foo/bar and foo/v2/bar, are independent, unrelated packages:

    • If both foo/bar and foo/v2/bar are imported (perhaps indirectly via other dependencies), Go will build and link both.

    • If only foo/bar is imported, Go will not build foo/v2/bar (Go has no concept of "use the newest major version").

    • The only way a program or library can upgrade a dependency to a newer major version is to edit the source code, bump the /vN major version suffix in the import statement, and rebuild.

XS-Go-Import-Path Field

The XS-Go-Import-Path field in the source package stanza of debian/control must be set to a comma-separated list of Go module paths for the packaged modules. The paths must include any major version suffix (/v2, etc.). For example:

Source: golang-github-coreos-go-oidc-v3
XS-Go-Import-Path: github.com/coreos/go-oidc/v3
...

The XS-Go-Import-Path field in debian/control causes a Go-Import-Path field to be added to the source control file (*.dsc) when the source package is built.

Note
TODO: Go-Import-Path is a misnomer; it actually holds multiple module paths (the value of the module directive in each module’s go.mod file), not a single package path used for an import declaration. A better name would be Go-Module-Paths.

Because the generated Go-Import-Path field is added to the source control (*.dsc) file, the field applies to the source package, not to any specific binary package produced by the source package. This has the unfortunate effect of making it difficult for dh-make-golang to determine which binary package to use as a dependency if the source package produces multiple binary packages (see One Binary Package Per Debian Source Package).

Note

TODO: Migrate from the XS-Go-Import-Path field in the source package stanza to per-binary XB-Go-Module-Paths fields in the binary package stanzas. This would make it possible for a source package to produce multiple binary packages without confusing dh-make-golang.

Unfortunately, the documentation for user-defined fields (Policy Manual 4.7.3) does not say that a per-binary value is supported; it can be interpreted as requiring all XB-* fields to be in the source package stanza, and that the value is copied to every binary package (see bug #1117566). However, it has been observed that each binary package can have its own XB-* field with a unique value that applies only to that binary package. dpkg 1.23 clarified that XB-* is intentionally supported in either the source package stanza or a binary package stanza, although it does not explicitly state that a XB-* field in a binary stanza applies only to that binary package, or that it overrides a same-named field in the source stanza.

Rationale
  • dh-make-golang uses the Go-Import-Path metadata to locate the Debian package that provides a dependency.

  • dh-golang uses the Go-Import-Path metadata to derive the installation location for the module’s source code files.

  • The major version suffix is required because the same Go module might be packaged multiple times (one Debian package per major version).

File locations

All source code files, including go.mod and go.sum, should be installed into /usr/share/gocode/src/${module_path}, where ${module_path} is the Go module’s module path. Note that GOPATH is set to /usr/share/gocode when dh-golang builds a Debian package.

Tip
If the XS-Go-Import-Path Field is set properly in debian/control, and only one Go module is being packaged (see One Go Library Module Per Debian Source Package), dh-golang will install the source code to the appropriate location.

If the module path has a major version suffix (/v2, etc.), that suffix must be included in the install path.

As an example, for github.com/coreos/go-oidc/v3 (golang-github-coreos-go-oidc-v3-dev), one of the files is /usr/share/gocode/src/github.com/coreos/go-oidc/v3/oidc/oidc.go. See https://packages.debian.org/sid/all/golang-github-coreos-go-oidc-v3-dev/filelist for the full file list.

Rationale
  • Including go.mod and go.sum allows Go to check required dependencies.

  • Including go.mod enables Go to reinterpret a filesystem pathname as a module path; see https://lists.debian.org/debian-go/2020/06/msg00003.html for discussion.

  • Including the major version suffix in the install path makes it possible to install multiple major versions of the same library at the same time.

Dependencies

Your library package, e.g. golang-github-lib-pq-dev, needs to have all the other Go libraries it depends on in its Depends line. The dependencies need to be available at build time to run the tests (if any) and at installation time so that other packages can be built.

Upstream package moves

Occasionally, upstream packages might move from one code hosting provider to a different one, as was the case with code.google.com being discontinued and many projects moving to GitHub.

Such a move should be mentioned in debian/changelog and a compatibility symbolic link should be installed using debian/links (see https://salsa.debian.org/go-team/packages/golang-go.crypto/commit/4e0a285a0989331edb5ff580797ab3574197534d for an example). Since the location of the package is also contained in the Debian package’s name, it should be renamed (see https://salsa.debian.org/go-team/packages/golang-go.net/commit/189085288e608ca2720b32d551227d330a561123 for an example).