How to Build Debian Packages

Last updated: December 14, 2023 | Arda Atci

Building a Debian package, commonly known as a .deb file, is an essential skill for software distribution on Debian-based Linux systems.

The process begins with setting up a structured directory for your package and creating a crucial control file. Using the dpkg-deb command, the package is assembled from this directory, resulting in a distributable .deb file.

In this guide, we learn how to build a Debian package step by step.

Step 1: Install Prerequisites

Before building a package, you'll need to install essential tools like build-essential, dh-make, debhelper, and devscripts.

Command to install above all packages:

sudo apt-get install build-essential dh-make debhelper devscripts

Step 2: Create a Working Directory

Create a directory for your project, for examples here I am using a directory named pdwmc:

mkdir pdwmc
cd pdwmc

Then place your application's source code in the pdwmc directory. Note: Ensure that your code follows best practices for Debian packaging.

Create a DEBIAN directory within your package directory:

mkdir DEBIAN

Key components

A .deb package is a fully built Debian package. It contains all the files and metadata about the program it contains. Debian uses a binary packaging strategy; thus a .deb package will have binary files that are previously compiled.

Let's explore the key components:

Control File: This is the main file used to build a Debian package which contains information about the package, such as name, version, description, dependencies, maintainer, etc.

Binary Files: Compiled files of the target program.

Configuration Files: A sample configuration of the program if necessary. Most of the time these files will be found in ‘/etc’ or sometimes in ‘/etc/xdg’.

Documentation: Documentation about the package can be found in ‘/usr/share/doc’. If the program has manual pages, they will be inserted in a subdirectory within ‘/usr/share/man’ or ‘/usr/local/share/man’.

Other Directories: Aside from the above, there might be some other directories like ‘/usr/share/<package>’; these are related to package data/image files or dependencies.

Step 3: Create Pre-Installation and Post-Installation Scripts (Optional)

Pre-Install and Post-Install scripts allow you to execute specific actions before and after the installation of the package, providing greater control and customization.

Pre-Inst: This is the pre-installation file. It contains the actions required before the package installation. If this file is present, it will be executed before any files are installed in the system.

Common Objectives:

  • Stopping services (systemd)
  • Downloading required files
  • Creating new services (systemd)

Post-Inst: This file will execute after all package files have been installed successfully into the system. This script helps package maintainers perform configurations and necessary tasks of the package after it is installed.

Common Objectives:

  • Service activation (systemd)
  • Configuration files
    • Back up older versions of configuration files
    • Install sample configuration files
  • User-group modification: If the package needs a specific user or group, this script will modify them on demand.
  • Clear temporary files after installation

Step 4: Organize Program Files

All the source files must be put into their respective directory. This is not a rule, but a specification that should be followed.

  • Executable files: /usr/bin
  • Program-related data files (image, ui etc.): /usr/share
  • Configuration files: /etc
  • Library objects: /usr/lib

Any user's home folder should not be managed with any kind of package. Home folder management is dependent only on the user, not on packages.

Step 5: Create Control File

The control file is the most important file in a Debian package. It is located in the ‘DEBIAN’ folder located in the root directory of the package.

Create the control file in the DEBIAN directory:

touch DEBIAN/control
nano DEBIAN/control

Structuring the Control File

The control file follows a simple variable: value syntax. Here are the key components:

  1. Package Information:
    • Package: The name of the package.
    • Version: The version of the package.
    • Section: Categorizes the package (e.g., editors, education, development).
    • Priority: Importance of the package (optional, required, important).
    • Maintainer: Name and email of the person maintaining the package.
    • Uploader: (Optional) Additional contributor information.
  2. Build and Dependency Information:
    • Depends: Lists runtime dependencies required by the package.
    • Recommends: Suggested packages that enhance functionality.
    • Suggests: Additional packages that could be useful.
    • Pre-Depends: Dependencies that must be satisfied before building the package.
    • Breaks: Specifies packages that are incompatible with this package.
    • Conflicts: Indicates packages that cannot coexist with this package.
    • Pre-Inst, Post-Inst, Pre-Rm, Post-Rm: (Optional) Paths to installation and removal scripts.
  3. Authenticity and Additional Information:
    • MD5sum, SHA1sum, SHA256sum: Checksums for verifying package integrity.
    • Architecture: The architecture for which the package is built (e.g., all, amd64, i386).

Here's a sample control file for a hypothetical libconfig package:

Package: libconfig
Version: 1.7.3-1
Section: utils
Origin: libconfig
Maintainer: <Package Maintainer>
Original-Maintainer:  <Package Owner>
Provides: libconfig
Depends: libc6 (>= 2.34)
Description: libconfig 11
Replaces: libconfig9
Architecture: all

Take a look at "Depends" field, to state a specific version dependency, the syntax for it will be (>= version) or (< version). This will force the package to search for a specific version rather than any version of target dependency.

The tree structure shows the organization of files within the package source directory pdwmc:

tree pdwmc
pdwmc
├── DEBIAN
 |   └── control
 |   └── preinst
 |   └── postinst
└── usr
    ├── bin
    │   └── pdwmc
    └── share
        └── pdwmc
            ├── dialog.ui
            ├── dwmparams.py
            ├── dwmparser.py
            ├── main.ui
            ├── pdwmc
            ├── pdwmgui.py
            ├── phyOS_simple_term_menu.py
            └── utils.py

6 directories, 10 files

Step 6: Build the Package

For building the Debian package we are using dpkg-deb command.

Syntax:

dpkg-deb —build <debian-source-directory> .

The command will build the package in the current directory. The . at the end of the command will build the package with the version and architecture name included. If it is omitted, the package will only have the base name with a .deb extension.

To build the package, run:

dpkg-deb --build pdwmc .

This will result in a .deb package named with the version and architecture, as specified in the control file (e.g., pdwmc_1.1-01_all.deb).

Output:

dpkg-deb: building package 'pdwmc' in './pdwmc_1.1-01_all.deb'.

Step 7: Sign the Package (optional)

Signing a Debian package is an optional step, this ensures its authenticity and integrity, especially if the package is going to be distributed publicly.

There are several ways to sign a Debian package, here I am using dpkg-sig script to sign the package.

Packages are signed with a gpg key. If you do not have a gpg key, create one:

gpg –-full-generate-key

It will ask several questions for key configurations, keep the defaults since this will be used for testing purposes only.

Note: If you're distributing your package publicly, you may also want to distribute your public GPG key so that users can verify the package's signature.

After creating a gpg key, install dpkg-sig package that is designed for signing Debian packages:

sudo apt install dpkg-sig 

Key identity is needed to be able to sign the package. To list gpg key ID:

gpg –list-secret-keys –keyid-format=long

Output should be similar to this:

/home/linuxopsys/.gnupg/pubring.kbx
------------------------------
sec   rsa3072/964FD85861C858D7 2022-06-04 [SC] [expires: 2024-06-03]
      F8FFF8A536F0F864F2BE3244964FD85861C858D7
uid                 [ unknown] rzero <[email protected]>

Key id is in the part that starts with the "sec" line. It is declared after the encryption algorithm, in this case, it is rsa3072/964FD85861C858D7. The remaining part after the ‘/’ is the key id, which is 964FD85861C858D7.

To sign the package, use the deb-sig script with the respective flags. Example:

dpkg-sig -k 964FD85861C858D7 –sign builder pdwmc_1.1-01_all.deb

Step 9: Verify the Package (Optional)

After signing the package, verify it by using dpkg-sig again.

Example:

dpkg-sig --verify pdwmc_1.1-01_all.deb

This will check for signatures in the specified .deb package.

Step 10: Add a Copyright File (Optional)

Adding a copyright file to your Debian package ensured that the licensing and copyright information is clear and accessible.

Create a new file called copyright in the root of the package source. 

Follow the base format provided. Here's an example of how it could be filled out for a package:

Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: <package-name>
Upstream-Contact: Name Surname <email>
Source: <source-link-to-package>

Files: *
Copyright: <year> Copyright Holder
License: <License>

An asterisk after the files section means all the files in the package have a copyright. Fill everything else according to your credentials and license preference. Do not forget to build the package each time if anything has been changed/added to the root directory.

Step 11: Test and Distribute

To test or verify a Debian package, use the dpkg or apt command. Note that the target package needs to be installed in the system for verification.

Example:

Install the package first, using apt or dpkg

$ apt install -f pdwmc_1.1-01_all.deb

Or using dpkg

$ dpkg -i pdwmc_1.1-01_all.deb

$ dpkg --verify pdwmc_1.1-01_all.deb
SHARE

Comments

Please add comments below to provide the author your ideas, appreciation and feedback.

Leave a Reply

Leave a Comment