Debian's app distribution (apt) is a simple, native, and friction-less tool to manage apps and
their dependencies. Just with the combo apt update && apt upgrade
you ensure that systems are
up-to-date, including your software and its dependencies —and it works with Ubuntu and friends.
In this tutorial we learn how to package apps, generating .deb files, and distribute them through apt by creating a simple repository.
Create a debian package
A Debian package is a collection of files and folders compressed in a special way. Specifically it contains:
- A folder called DEBIAN that contains:
- A file called control, that has information about the package (like the name, the version...).
- Optional scripts to execute during different stages of the installation.
- Other files and folders that apt will just copy to the system where you are installing it. The
root of the package mimics the root of the computer you are installing it, and anything you put
in the package will be copied exactly. For example, if you want
/etc/foo
in your system you create a folder called etc in your package and add a file foo inside.
An example for a package is as follows:
In the package above we can see the required DEBIAN/control file, a postinst script (which we
will analyze later), and then an etc/my-program/LICENSE
and etc/my-program/config
, two files that will be copied to the system, automatically creating
the directory /etc/my-program
in the process.
So, to create a package start with an empty folder and just add those files.
The DEBIAN/control file
The DEBIAN/control file includes metadata about the package. Items such as the package name, version number, and dependencies are specified here. An example with an explanation is as follows:
- Package: The name of the package. It must follow the Debian naming conventions.
- Version: The version of the package, explained below.
- Architecture: If your package contains compiled code, the architecture for this binary,
otherwise
all
. - Maintainer: An RCF 822 e-mail address.
- Depends: A comma-separated list of packages that are dependencies, which apt installs before installing your software.
- Description: A free-form, human-readable text.
Get here more information and options for the control file.
Versioning
The version number is a sensible part in the package, as a wrong value can make apt to upgrade the package to an older version, which is difficult to debug and fix later. The version number must follow Debian versioning conventions.
However, such convention is quite open, so we have had better results by following the more
restrictive version scheme of the Debian official repositories, simplified
as <software version>-<debian revision>
. A 1.3-2
means that is the software version 1.3 and the
version 2 of the specific installation files and scripts. A 1.3-3
means that we changed the
installation scripts and files, but it is still based on the 1.3 version of the software.
You can compare versions and check their correctness
with dpkg --compare-versions <v1> <comparator> <v2>; echo $?
, outputting 0 if it is True. The
comparators are lt le eq ne ge gt
. An example
is dpkg --compare-versions 1.1-1 lt 1.1-2; echo $?
. You can get more info in
the man page of dpkg (look for --compare-versions).
Finally, the following example illustrates the hierarchy of
versions: 1.9 < 1.12, 1.1~b1 < 1.1, 1.1 < 1.1-1 < 1.1-2
.
Mantainer scripts
As we saw above, we can add scripts files in the DEBIAN folder. We can have up to four script files, called _preinst, postinst, prerm,and_postrm, representing the stage of the installation when the script is executed.
- preinst executes before unpacking the files.
- postinst executes after unpacking the files.
- prerm executes before deleting the files when uninstalling.
- postrm executes after removing the files when uninstalling.
Maintainer scripts are regular shell scripts with the following characteristics:
- They must be idempotent, as they can be called several times. Because of it, add guards and prefer operations that are already idempotent (such as chown, systemctl enable|disable)
- They must detect errors and propagate them upwards. If the script cannot perform its tasks, it
has to notify the package system via its return code. In practice, we just add
set -e
at the beginning of the script. - They can be written in any scripting language by using a shebang, as long as you ensure that such interpreter is available ( see pre-depends).
An example of a script is as follows:
Here you have more information.
Building the .deb
Once you have the folders and files of your future package, build it by
executing dpkg-deb -b <path/to/source/dir> <destination/dir>
. This results in a .deb file
called package_version_arch.deb. You can install it with apt install <deb-file.deb>
.
Setting up an apt repository
A Debian repository is an HTTP server that allows to access the .deb files in a regular folder of
your server machine. This folder includes too a special Packages.gz file that is an index of the
packages your repository offers, which apt gets when performing apt update
.
For this example we use Apache to setup the repository.
1. Install apache with apt install apache2
.
2. Create a file in /etc/apache2/sites-available/packages.my.org.conf
with the following
contents:
/path/to/repo
is the path you want your repository files to be in. An example
is /home/<user>/repository/
.
3. Give the apache user access to the folder. A quick way is by adding apache to the user group
that created the folder: adduser www-data my-user
.
4. Enable the site: a2enmod packages.my.org
.
5. Restart apache: apachectl restart
.
6. Go to the URL of your repository with a browser to see an empty directory. Otherwise you should
fix your configuration.
7. Move the .deb files you built before to /path/to/repo
, and do not change the
name dpkg-deb
gives them.
8. Run dpkg-scanpackages --multiversion -- /path/to/repo | gzip -9c > /path/to/repo/Packages.gz
,
which generates a suitable index file for apt update
.
Redo step 6 and 7 every time you add new packages.
Now, in every computer you want to install packages from the repository, add the following line, as
usual, to /etc/apt/sources.list
: deb http://url.to.my.repo /
.
apt install / upgrade will fail because the repository is not signed. Use apt-get install / upgrade instead and ignore the warning. Signed repositories are more difficult to create and, thus, are not part of this introductory tutorial. I would appreciate a guide or tutorial that explains it so I can link them here, or how to make apt work with unsigned repositories.
The original post was made by Daniel A. and it has been updated since by bustawin.