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.debpackages. -
APT (Advanced Package Tool): the high-level package management system
(package management system) that automatically resolves dependencies and
works with remote repositories, using
dpkgunderneath.
In practice:
-
dpkg -i something.debinstalls a local package. If dependencies are missing, the installation fails anddpkglists them. -
apt-get install somethingdownloads the package from the configured repositories, resolves dependencies, and then delegates installation todpkg.
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-debto/usr/local/bin/hola-deb. - Register the package as
hola-debversion1.0.0. -
Declare a dependency (for example,
bash) to see how theDependsfield 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 (thecontrolfile 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 thecontrolfile and optional scripts (such aspostinst,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,optionalis usually fine. -
Architecture: target architecture (amd64,arm64orallfor architecture-independent packages such as scripts). -
Depends: list of dependencies that must be installed for the package to work. During installation,dpkgwill 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 adebian/directory withdh_makeand build withdpkg-buildpackageordebuild. This gives you:- Clear declaration of build dependencies.
- Changelogs in Debian format.
-
Integration with tools such as
pbuilderorsbuildfor 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:
to detect errors, warnings, and improvements according to Debian policy.lintian hola-deb_1.0.0_amd64.deb -
Multi-architecture
UseArchitecture: allfor 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:
- Create the installation tree.
- Write the
DEBIAN/controlfile. - Build with
dpkg-deb --build. -
Install and test with
dpkg -i,dpkg -L,dpkg -s,dpkg -r, anddpkg -P. - 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