Building debian packages

Debian packages (.deb files) are the packages installed by apt and apt-get in ubuntu. They can be installed manually using the dpkg command or hosted in a PPA as described here.

Installing software

You will need to have the following software installed on a Linux based system:

  1. build-essential
  2. software-properties-common
  3. devscripts
  4. debhelper
apt-get install -y \
	build-essential \
	software-properties-common \
        devscripts \

Nice to haves:

  1. lintian
apt-get install -y \

2. git (latest version from the git-core PPA)

add-apt-repository ppa:git-core/ppa
apt update
apt install -y git

Cross building for RPI4?

  1. crossbuild-essential-arm64
apt-get install -y \

Anatomy of a debian package

The basic structure of a debian package consists of a number of files, you can see the full documentation here. There are some requirements on folder structures too, specifically around version numbers and where the debian package files reside.

  1. Make a folder with the named formatted as <your package name>-<version number> for example: my-awesome-package-0.0.1. You can read more about the version numbering here, but the above is sufficient
  2. Make a subfolder called debian
  3. Inside the debian folder a source/format file with 3.0 (native) as the text indicating a debian specific package
  4. Inside the debian folder a compat file with 10 as the text – this is the debhelper compatibility version

control file

This is the heart of the definition of your package, you can specify values for all the fields shown by the apt command when installing and interrogating your package. You can see a detailed description of this file here. For a simple build:

Source: my-awesome-package
Section: misc
Priority: optional
Maintainer: Me <>
Standards-Version: 3.9.7

Package: my-awesome-package
Architecture: amd64
Essential: no
Description: Does awesome things to your computer

rules file

This file defines how the debhelper application will build your package. The simplest definition is:

#!/usr/bin/make -f


	dh $@

If you are packaging your application as a SystemD unit:

#!/usr/bin/make -f


	dh $@ --with systemd

	dh_systemd_enable -pmy-awesome-package --name=my-awesome-package my-awesome-package.service
	dh_installinit -pmy-awesome-package --no-start --noscripts
	dh_systemd_start -pmy-awesome-package --no-restart-on-upgrade

	echo "Not running dh_systemd_start"

Make sure you replace my-awesome-package with the actual name of your package and include the .service file in your debian folder.

Adding files to the package

This is done using a .install file, you will need to be very careful with the name. It is <package name>.install. Continuing the above theme that would be my-awesome-package.install. This can be used to copy files in to the debian package and specify where on the target machine they will be placed. The format is 1 item per line with the source first, destination last separated by a space. The source is relative to the debian directory. You can use wildcards, for a react frontend application installed to a running apache instance as the only site, I have used this:

../*.json var/www/html
../*.js var/www/html
../*.ico var/www/html
../*.html var/www/html
../*.png var/www/html
../*.txt var/www/html
../static var/www/html


The final file is the changelog you can see the details of the composition here. I personally like to generate this from the tag history of my repositories as that is how a initiate deb package builds in my CI pipeline:

VERSION=$(git tag --sort=-taggerdate --format "%(refname:lstrip=-1)" | head -1)
git tag --sort=-taggerdate \
        --format "my-awesome-package (%(refname:lstrip=-1)) focal; urgency=medium%0a%0a  * %(subject)%0a%0a -- %(taggername) %(taggeremail)  %(taggerdate:rfc2822)" \
    > my-awesome-package-"$VERSION"/debian/changelog

There is a bit going on here, refname:lstrip=-1 is getting the last portion of the tag name, so for 0.0.1 it is 0.0.1, for development/0.0.1 it is 0.0.1 too. The %0a text is line breaks, the tag date has to be formatted as rfc2822 to be compatible with the deb package.


You should now have a folder structure similar to this:

|   └───source
│   │   │   format
│   │   my-awesome-package.install
│   │   my-awesome-package.service # if installing as system d unit
│   |   compat
│   |   control
│   |   rules

Building the package

You should now be able to cd in to the my-awesome-package-0.0.1 directory and run dpkg-buildpackage. This will produce


If you are building for an RPI4, the command is:

CONFIG_SITE=/etc/dpkg-cross/cross-config.amd64 DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -aarm64 -Pcross,nocheck


This whole process has been formalised in a GitHub action here for your convenience

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: