How to create a Debian package for Ubuntu, Pop!_OS, Arch, etc.
Debian Linux is one of (if not the) most popular Linux distribution base, with distros such as Ubuntu, Pop!_OS, and Arch being built on them. There's many different tools associated with building debian packages, which can make it challenging to know where to start when first attempting to create a package. 2 main tools suited for this task are dpkg-deb
and debuild
.
dpkg-deb
dpkg-deb
is great tool to get started with when creating debian packages, an all that's required is creating a folder with your package assets and a debian
directory defining the control and changelog files. This makes it easy to get started, but as soon as you want to do something more complex this quickly becomes too simple. Enter debuild
.
debuild
debuild
is actually a wrapper over a suite of different programs used to generate debian packages that are meant to be distributed publicly. It handles a lot of the package building process for us so we don't have to run multiple commands to build a package. This tool requires a few files within a debian
folder within your project files.
debian/control file
The debian/control
file is required for all debian packages as it defines metadata about the package being built. For example:
Source: my-awesome-package
Priority: optional
Maintainer: Ryan Mellmer
Homepage: foobar.com
Depends: docker.io (>= 21.00.0)
Package: my-awesome-package
Architecture: amd64
Description: Ryan's awesome new package
This file is specifying info for a new package called my-awesome-package
, shows who authored it, and a description for the package.
Dependencies in the control file
You'll also notice a "Depends" section, where we can define which packages need to exist for this package to work, including any specific version requirements for those packages. Here, we're defining that docker.io version >= 21.00.0 is required.
When apt install
is ran to install our package, apt will attempt to also install any missing dependencies for the package. Note that apt will not by default ever install a lower version number of a package than what is the latest, so if you have specified a specific dependency version with a =
(i.e. docker.io (= 21.00.0)
, notice the missing >
), then apt will only install that package for you if 21.00.0 is indeed the latest version in the repo. Otherwise, you'll get this error:
The following packages have unmet dependencies:
my-awesome-package : Depends: docker.io (= 21.00.0) but 23.10.0 is to be installed
This means that the package requires version 21.00.0, but apt will only install the latest, which is 23.10.0.
There are two ways around this at install time:
- Install the packages manually (i.e.
sudo apt install docker.io=21.00.0
) before installing your new package. - Pin the dependency versions in a new file within
/etc/apt/preferences.d/*
. An example file continuing with the example from above could be:
Package: docker.io
Pin: version 21.00.0
Pin-Priority: 999
The existence of this file tells apt to always prefer installing version 21.00.0 of the docker.io package instead of whatever the latest is.
debian/rules file
The debian/rules
file is a Makefile that defines how the package will be built. debuild
will call various make targets within in this file during the build process.
The most simple debian/rules
file could look something like:
#!/usr/bin/make -f
%:
dh $@
This rules file will just pass all make targets called by debuild
over to the dh (debhelper)
equivalent. Various steps within the dh
build process can be captured and overridden within the rules file.
Overriding dh build commands in the rules file
As mentioned previously, we can override functionality of various dh commands in our debian/rules
file. For example, if we wanted to override the dh_auto_install
step (which defines how files get added or installed to the package at build time), we can add the following to the rules file:
#!/usr/bin/make -f
PKG_DIR = debian/my-awesome-package
%:
dh $@
# Define which files to install, paths relative to ..
override_dh_auto_install:
mkdir -p ${PKG_DIR}/root/
install -m 750 ./my-script.sh ${PKG_DIR}/root/my-script.sh
...
This override_dh_auto_install
step will include a shell script called my-script.sh
to the /root/my-script.sh
directory within our package directory. When the resulting debian package is installed, this script will be installed on the system at /root/my-script.sh
.
Inspect files within .deb package
After building a deb package via debuild
, the dpkg-deb
command can be used to get a list of all files included in the package:
> dpkg-deb -c my-awesome-package.deb
...
drwxr-xr-x root/root 0 2023-02-02 19:06 ./root/
-rwxr-xr-x root/root 7136 2023-02-02 19:06 ./root/my-script.sh
...
Run debuild without signing
By default, debuild requires a GPG key for signing your package. However, for local use and testing, you can run debuild without signing the package by running:
debuild -b -uc -us
Generate a GPG key for your package
Eventually you'll probably want to sign your package with a gpg key if you want to publish it. You can generate a new key locally by running:
gpg --generate-key
This will prompt you for a "real name" and an email address. Email address can be left blank if desired. Then, will prompt for a passphrase to protect the key with.
How to pass Environment Variables into debian/rules
By default, debuild
sanitizes environment variables when running the build process (see this link. However, you can still pass specific environment variables through and used throughout the debian/rules
file using the -e
flag. For example, when calling debuild:
debuild -e VERSION=1.0.0
The VERSION
variable will be available for use in debian/rules
, and can be used like any other Makefile environment variable. In the following example, we override dh_gencontrol in the rules file to pass the VERSION variable to the debian/control
file:
#!/usr/bin/make -f
# VERSION=x.x.x debuild
%:
dh $@
# Set the Version: in DEBIAN/control with specified version
override_dh_gencontrol:
dh_gencontrol -- -v$(VERSION)
Finally, we modify the control file to use this new version
variable:
Source: my-new-package
Priority: optional
Maintainer: Ryan Mellmer
Version: ${version}
Homepage: foobar.com
Package: my-new-package
Architecture: amd64
Description: Ryan's awesome new package
Cleaning up
The dh_clean
can be used to clean up local build artifacts after debuild
has been ran.
Where to go from here?
While I hope this is a good place to start tinkering with debian packaging, there's tons more for me to learn. The official debian docs are a good place to dive deeper into building debian packages.