← ← Back to all posts

Packaging your application for Debian and derivatives: First .deb step by step

2025-11-25 · Benja

How to package an application into a .deb package for Debian, Ubuntu, and derivatives. Covers the role of dpkg and apt, the internal structure of a .deb, creating the installation tree, the DEBIAN/control file, building with dpkg-deb, installation/uninstallation testing, and advanced options such as command-line scripts

Packaging your application for Debian and derivatives: First .deb step by step

Distributing software on Linux goes far beyond uploading a .tar.gz with a README that says “copy this to /usr/local/bin and good luck”. The Debian ecosystem, with decades of evolution, provides a package management system that guarantees clean installations, safe upgrades, and complete removals.

In line with the package management objectives of the LPIC-1 certification, in this article we will create a functional .deb package: installable with dpkg -i, visible in the package database, and removable without leaving traces. We will not yet cover the full official Debian workflow (with dpkg-buildpackage or debuild), but we will walk through a solid path to package internal or third-party software for Debian, Ubuntu, and derivatives.

1. Quick context: dpkg vs apt

As described in LPIC-1 objective 102.4, the Debian ecosystem is built on two key components:

  • dpkg: the essential low-level tool (essential utility) that installs, configures, lists, and removes .deb packages.
  • APT (Advanced Package Tool): the high-level package management system (package management system) that automatically resolves dependencies and works with remote repositories, using dpkg underneath.

In practice:

  • dpkg -i something.deb installs a local package. If dependencies are missing, the installation fails and dpkg lists them.
  • apt-get install something downloads the package from the configured repositories, resolves dependencies, and then delegates installation to dpkg.

Our first .deb will initially be consumed with dpkg -i. Later on, we will see how to publish it in a repository so that apt can manage it like any other package.

2. The example application

We will start from a minimal CLI application:

  • Name: hola-deb
  • Build output: an executable hola-deb

Goals for our package:

  • Install hola-deb to /usr/local/bin/hola-deb.
  • Register the package as hola-deb version 1.0.0.
  • Declare a dependency (for example, bash) to see how the Depends field works.

3. Minimal anatomy of a .deb

A .deb package is internally an ar archive that contains at least these components:

  • debian-binary: a text file with the format version (for example, 2.0).
  • control.tar.{gz,xz}: package metadata (the control file and maintenance scripts).
  • data.tar.{gz,xz}: the files that will be installed into the filesystem.

We are not going to manipulate these files manually. Instead, we will use dpkg-deb to build the package from:

  • A directory tree that mirrors the target filesystem (for example, usr/local/bin, usr/share/doc, etc.).
  • A DEBIAN/ directory containing the control file and optional scripts (such as postinst, prerm, etc.).

4. Preparing the build environment

On a typical Debian or Ubuntu system, install the basic packaging tools. As a user with administrative privileges:

sudo apt-get update
sudo apt-get install dpkg-dev debhelper lintian

These tools provide dpkg-deb, helper utilities, and lintian, which will help you validate the quality of the package.

5. Building the installation tree

We start by creating a working directory and the installation structure:

mkdir -p ~/paquetes/hola-deb
cd ~/paquetes/hola-deb

mkdir -p paquete/usr/local/bin
mkdir -p paquete/usr/share/doc/hola-deb
mkdir -p paquete/DEBIAN

Then we copy the executable and add some basic documentation:

cp /ruta/a/tu/build/hola-deb paquete/usr/local/bin/
chmod 755 paquete/usr/local/bin/hola-deb

cat > paquete/usr/share/doc/hola-deb/README << 'EOF'
hola-deb: minimalistic Debian package example.
EOF

gzip -9 paquete/usr/share/doc/hola-deb/README

Everything you place under paquete/ will be installed preserving that same structure starting from the root of the system (/).

6. The heart of the package: DEBIAN/control

The DEBIAN/control file defines the package metadata. Let’s create it with minimal but correct content:

Package: hola-deb
Version: 1.0.0
Section: utils
Priority: optional
Architecture: amd64
Depends: bash (>= 5.0)
Maintainer: Your Name <you@email>
Description: Minimalist Debian package example
 Packages a hola-deb binary into /usr/local/bin to illustrate
 the process of building a .deb from scratch.

Key fields you should understand well:

  • Package: package name, lowercase and with no spaces.
  • Version: package version; ideally follow semantic versioning (MAJOR.MINOR.PATCH).
  • Section: package category (for example, utils, net, devel).
  • Priority: relative importance (required, important, optional, etc.). For in-house software, optional is usually fine.
  • Architecture: target architecture (amd64, arm64 or all for architecture-independent packages such as scripts).
  • Depends: list of dependencies that must be installed for the package to work. During installation, dpkg will verify these dependencies and fail if they are not met.
  • Description: short first line; subsequent lines must be indented with one space and expand on the description.

7. Building the .deb with dpkg-deb

With the structure ready, we can build the package:

dpkg-deb --build paquete
mv paquete.deb hola-deb_1.0.0_amd64.deb

Before installing it, it is good practice to inspect its contents using the utilities required by LPIC-1:

dpkg-deb -I hola-deb_1.0.0_amd64.deb  # Metadata (control)
dpkg-deb -c hola-deb_1.0.0_amd64.deb  # List of included files

8. Testing the package: install, list and remove

8.1 Installing with dpkg -i

We install the package directly:

sudo dpkg -i hola-deb_1.0.0_amd64.deb

If dependencies are missing, dpkg will fail and show which ones are required. You can fix this with:

sudo apt-get -f install

The -f (fix-broken) flag makes APT install missing dependencies and complete configuration of pending packages.

8.2 Verifying the installation

To list all files installed by the package (a key command for auditing according to LPIC-1), use:

dpkg -L hola-deb

To view information about the installed package (status, version, description, etc.), use dpkg -s:

dpkg -s hola-deb

8.3 Removal and purge

The LPIC-1 material emphasizes the difference between removing and purging a package. In Debian:

  • dpkg -r (remove): removes package files but leaves configuration files.
  • dpkg -P (purge): also deletes configuration files.

Applied to our example:

sudo dpkg -r hola-deb    # Removes the package, leaves config (if any)
sudo dpkg -P hola-deb    # Purges all traces of the package

9. Adding maintenance scripts (optional)

Maintenance scripts in DEBIAN/ are executed at different stages of the package life cycle:

  • preinst: before unpacking/installing.
  • postinst: after installation and configuration.
  • prerm: before removal.
  • postrm: after removal or purge.

Simple example of a postinst for our package:

cat > paquete/DEBIAN/postinst << 'EOF'
#!/bin/sh
set -e

echo "Thanks for installing hola-deb."
echo "Run 'hola-deb' to greet the system."

exit 0
EOF

chmod 755 paquete/DEBIAN/postinst

Rebuild the package and install it again to see the message during the configuration phase. In real projects, these scripts are used for tasks such as regenerating caches, creating system users, or migrating configuration files between versions.

10. Optional: serving the package via APT

Installing with dpkg -i works for testing or one-off deployments, but in larger environments it is much more convenient to expose the package in an APT repository (even a local one).

Create a directory for the repository and add the package:

sudo mkdir -p /srv/debian-repo/pool/main/h/hola-deb
sudo cp hola-deb_1.0.0_amd64.deb /srv/debian-repo/pool/main/h/hola-deb/

Generate the package index:

cd /srv/debian-repo
sudo mkdir -p dists/stable/main/binary-amd64
dpkg-scanpackages pool /dev/null | gzip -9c > dists/stable/main/binary-amd64/Packages.gz

Add the repository to /etc/apt/sources.list:

deb [trusted=yes] file:/srv/debian-repo stable main

Update the index and install the package as you would any other:

sudo apt-get update
sudo apt-get install hola-deb

From this point on, APT will take care of resolving dependencies, updating and removing your package just like packages from official repositories.

11. Best practices to go further

  • Adopt the full Debian packaging infrastructure
    Create a debian/ directory with dh_make and build with dpkg-buildpackage or debuild. This gives you:
    • Clear declaration of build dependencies.
    • Changelogs in Debian format.
    • Integration with tools such as pbuilder or sbuild for reproducible builds.
  • Integrate with CI/CD
    Automate package builds on every tag in your repository (GitHub Actions, GitLab CI, etc.) and publish artifacts to an internal APT repository.
  • Use lintian
    Run:
    lintian hola-deb_1.0.0_amd64.deb
    
    to detect errors, warnings, and improvements according to Debian policy.
  • Multi-architecture
    Use Architecture: all for script-only packages and build architecture-specific packages (amd64, arm64, etc.) when distributing binaries.

12. Conclusion

Packaging your application for Debian is not just about producing a .deb that “more or less installs”. It is about speaking the native language of dpkg and apt: properly declared dependencies, a coherent filesystem layout, and maintenance scripts that respect the package life cycle.

With the workflow we covered:

  1. Create the installation tree.
  2. Write the DEBIAN/control file.
  3. Build with dpkg-deb --build.
  4. Install and test with dpkg -i, dpkg -L, dpkg -s, dpkg -r, and dpkg -P.
  5. Optionally add scripts and publish in an APT repository.

You now have a fully functional .deb, aligned with the package management concepts evaluated in the LPIC-1 certification. The natural next step is to move to the “official” workflow with debian/ and dpkg-buildpackage, treating your packages as first-class citizens on any Debian or derivative. https://wiki.debian.org/es/Packaging