Debian Packaging
Creating and Managing .deb Packages
Section titled “Creating and Managing .deb Packages”0. Specs
Section titled “0. Specs”0.1. The What
Section titled “0.1. The What”This tutorial covers the creation of a .deb package. A .deb package is a compressed archive of files used to install software on Debian GNU/Linux and Ubuntu systems.
As Debian or Ubuntu users, we typically use the apt command to install programs or services. In the background, .deb files are the actual packages being installed.
Our goal is to create and use a .deb package. Note that creating a completely new Debian package from scratch is a broader topic—a superset of what we’ll cover here—and could be the subject of a separate tutorial.
This tutorial is introductory and does not cover every detail. Its purpose is to help you create a simple package.
0.2. The Environment
Section titled “0.2. The Environment”Workstation: Debian 13 or Ubuntu 24.04 LTS
Test Servers: Ubuntu 24.04 LTS, Ubuntu 22.04 LTS, Debian 13, Debian 12.
0.3. Sources
Section titled “0.3. Sources”1. Basic Information
Section titled “1. Basic Information”1.1. Package Types:
Section titled “1.1. Package Types:”.deb packages can be either source packages or binary packages. As the names suggest, source packages contain source code, while binary packages contain compiled executables or scripts.
According to the Debian Policy Manual: A Debian source package contains the source material used to construct one or more binary packages.
In this tutorial, we will focus on binary packages.
1.2. Package Structure
Section titled “1.2. Package Structure”A .deb package follows a specific naming format and contains three main files:
Format: packagename_version-revision_architecture.deb
Example: apache2_2.4.52-1debian4.4_amd64.deb
Internal files:
-
debian-binary
Contains the Debian package format version, typically just2.0. -
data.tar.zst
A compressed archive of files organized in directory structure. When the package is installed, these files are extracted to their corresponding locations in the filesystem. The extension may vary if a different compression method is used (e.g.,.gz,.xz). -
control.tar.zst
A compressed archive of control and maintenance scripts. Common files include:control: Metadata about the package (name, version, dependencies, etc.)conffiles: A list of configuration files that should be preserved during upgradespreinst: Scripts executed before the package is unpacked (e.g., stopping services before an upgrade)postinst: Scripts executed after the package is unpacked (e.g., starting services, updating system caches)prerm: Scripts executed before the package is removed (e.g., stopping services)postrm: Scripts executed after the package is removed (e.g., cleaning up temporary files)
1.3. .deb Package Manipulation
Section titled “1.3. .deb Package Manipulation”.deb packages can be installed directly using apt:
Install a package:
sudo apt install ./<package>sudo apt install ./test.debRemove a package:
sudo apt remove <appname>sudo apt remove testPurge a package (removes configuration files as well):
sudo apt purge <appname>sudo apt purge test2. Package Control Files
Section titled “2. Package Control Files”Control files are located in the control.tar.zst archive. Some key files were listed in Section 1.2. Below is a more comprehensive list with brief descriptions; important files are covered in greater detail.
2.1. control
Section titled “2.1. control”This file contains metadata used by package managers. It consists of fields and their corresponding values.
Most fields have single-line values, but some support multiple lines. For multi-line fields, each line after the first must begin with a space or tab.
Fields in a binary package:
-
Package(Mandatory): Name of the package. May contain alphanumeric characters, hyphens (-), and periods (.).
Example:openssh-server -
Source: Name of the source package.
Example:openssh -
Version(Mandatory): Package version. Format:[epoch:]upstream_version-debian_revision. The epoch is optional.
Example:1:8.9p1-3 -
Section(Recommended): Software category (e.g.,admin,database,httpd,java). -
Priority(Recommended): Indicates the importance of the package:required: Essential for system functionality.important: Expected on every Linux system.standard: Installed by default; must not conflict with other standard packages.optional: Most packages fall here; may conflict with others.
-
Architecture(Mandatory): Target architecture (e.g.,amd64,arm64,i386,powerpc,sparc,all). -
Essential: If set toyes, the package manager will refuse to remove it. Usually set tonoor omitted. -
Depends: Absolute dependencies; this package will not be configured unless all listed packages are configured. -
Pre-Depends: LikeDepends, but dependencies must be configured before unpacking this package (use sparingly). -
Recommends: Strong but optional dependencies. -
Suggests: Suggested packages that enhance functionality. -
Enhances: Inverse ofSuggests; indicates that other packages are more useful with this package. -
Breaks: This package breaks listed packages; they must be unconfigured before installation. -
Conflicts: Stronger thanBreaks; conflicting packages must be removed before installation. -
Installed-Size: Estimated disk space (in kilobytes) required after installation. -
Maintainer(Mandatory): Name and email of the maintainer.
Example:Exforge <exforge@x386.org> -
Description(Mandatory): Package description. The first line is a short summary; subsequent lines provide detailed information. -
Homepage: URL of the package’s website. -
Built-Using: Packages required during build but not runtime. -
User-defined fields may also appear. Ubuntu adds
Original-Maintainerto preserve Debian maintainer information.
Example: apache2 control file (Debian 13):
Package: apache2Version: 2.4.65-2Architecture: amd64Maintainer: Debian Apache Maintainers <debian-apache@lists.debian.org>Installed-Size: 576Pre-Depends: init-system-helpers (>= 1.54~)Depends: apache2-bin (= 2.4.65-2), apache2-data (= 2.4.65-2), apache2-utils (= 2.4.65-2), media-types, procps, perl:anyRecommends: ssl-certSuggests: apache2-doc, apache2-suexec-pristine | apache2-suexec-custom, ufw, www-browserProvides: httpd, httpd-cgiSection: httpdPriority: optionalHomepage: https://httpd.apache.org/Description: Apache HTTP Server The Apache HTTP Server Project's goal is to build a secure, efficient and extensible HTTP server as standards-compliant open source software. The result has long been the number one web server on the Internet. . Installing this package results in a full installation, including the configuration files, init scripts and support scripts.Example: openssh-server control file (Debian 13):
Package: openssh-serverSource: opensshVersion: 1:10.0p1-7Architecture: amd64Maintainer: Debian OpenSSH Maintainers <debian-ssh@lists.debian.org>Installed-Size: 3429Pre-Depends: init-system-helpers (>= 1.54~)Depends: libpam-modules, libpam-runtime, lsb-base, openssh-client (= 1:10.0p1-7), openssh-sftp-server, procps, ucf, debconf (>= 0.5) | debconf-2.0, runit-helper (>= 2.14.0~), systemd | systemd-standalone-sysusers | systemd-sysusers, libaudit1 (>= 1:2.2.1), libc6 (>= 2.38), libcom-err2 (>= 1.43.9), libcrypt1 (>= 1:4.1.0), libgssapi-krb5-2 (>= 1.17), libkrb5-3 (>= 1.13~alpha1+dfsg), libpam0g (>= 0.99.7.1), libselinux1 (>= 3.1~), libssl3t64 (>= 3.0.0), libwrap0 (>= 7.6-4~), libwtmpdb0 (>= 0.13.0), zlib1g (>= 1:1.1.4)Recommends: default-logind | logind | libpam-systemd, ncurses-term, xauthSuggests: molly-guard, monkeysphere, ssh-askpass, ufwConflicts: sftp, ssh-socks, ssh2Breaks: runit (<< 2.1.2-51~)Replaces: openssh-client (<< 1:7.9p1-8), ssh, ssh-krb5Provides: ssh-serverSection: netPriority: optionalMulti-Arch: foreignHomepage: https://www.openssh.com/Description: secure shell (SSH) server, for secure access from remote machines This is the portable version of OpenSSH, a free implementation of the Secure Shell protocol as specified by the IETF secsh working group. . Ssh (Secure Shell) is a program for logging into a remote machine and for executing commands on a remote machine. It provides secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. It can be used to provide applications with a secure communication channel. . This package provides the sshd server. . In some countries it may be illegal to use any encryption at all without a special permit. . sshd replaces the insecure rshd program, which is obsolete for most purposes.2.2. conffiles
Section titled “2.2. conffiles”Lists configuration files that should be preserved during upgrades. Users are warned if they have modified these files.
Example from openssh-server (Ubuntu 22.04 LTS):
/etc/default/ssh/etc/init.d/ssh/etc/pam.d/sshd/etc/ssh/moduli/etc/sv/ssh/.meta/installed/etc/sv/ssh/finish/etc/sv/ssh/log/run/etc/sv/ssh/run/etc/ufw/applications.d/openssh-server2.3. preinst
Section titled “2.3. preinst”Script executed before unpacking the package. Must be idempotent (can run more than once without any problems), start with a shebang (#!), and return 0 on success.
Call scenarios:
new-preinst install– Before initial installation.new-preinst install old-version new-version– Before upgrading a removed package.new-preinst upgrade old-version new-version– Before package upgrade.old-preinst abort-upgrade new-version– Ifpostrmfails during upgrade.
Excerpt from apache2 preinst:
case "$1" in upgrade|install)
if dpkg --compare-versions "$2" lt-nl "2.4.23-3~" ; then list_fixup_conffiles | replace_broken_conffiles fi
;;
abort-upgrade) list_fixup_conffiles | revert_broken_conffiles ;;
*) echo "preinst called with unknown argument \`$1'" >&2 exit 1 ;;esac2.4. postinst
Section titled “2.4. postinst”Script executed after unpacking the package. Must be idempotent, start with a shebang, and return 0 on success.
Call scenarios:
postinst configure old-version– After installation.old-postinst abort-upgrade new-version– Ifprermfails during upgrade.old-postinst abort-remove– Ifprermfails during removal.postinst abort-deconfigure in-favour new-package new-version [...]– Ifprermfails during deconfiguration due to conflict.
Excerpt from apache2 postinst:
is_fresh_install(){ if [ -z "$2" ] ; then return 0 fi return 1}case "$1" in configure) enable_default_mpm $@ install_default_files $@ enable_default_modules $@ enable_default_conf $@ install_default_site $@ execute_deferred_actions ;; abort-upgrade) ;; abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;;esac2.5. prerm
Section titled “2.5. prerm”Script executed before removing a package. Must be idempotent, start with a shebang, and return 0 on success.
Call scenarios:
prerm remove– Before removal.old-prerm upgrade new-version– Before upgrade.new-prerm failed-upgrade old-version new-version– If upgrade fails.prerm deconfigure in-favour new-package new-version– Before deconfiguration due to conflict.prerm remove in-favour new-package new-version– Before replacement due to conflict.
Example from mariadb-server prerm:
#!/bin/shset -e# Automatically added by dh_installsystemd/13.24.2if [ -z "$DPKG_ROOT" ] && [ "$1" = remove ] && [ -d /run/systemd/system ] ; then deb-systemd-invoke stop 'mariadb.service' >/dev/null || truefi# End automatically added section# Automatically added by dh_installinit/13.24.2if [ -z "$DPKG_ROOT" ] && [ "$1" = remove ] && [ -x "/etc/init.d/mariadb" ] ; then invoke-rc.d --skip-systemd-native mariadb stop || exit 1fi# End automatically added section2.6. postrm
Section titled “2.6. postrm”Script executed after removing a package. Must be idempotent, start with a shebang, and return 0 on success.
Call scenarios:
postrm remove– After removal.postrm purge– After purging.old-postrm upgrade new-version– After upgrade.new-postrm failed-upgrade old-version new-version– If upgrade fails.postrm disappear overwriter-package overwriter-version– After files are overwritten.new-postrm abort-install– Ifpreinstfails during installation.
Excerpt from openssh-server postrm:
if [ "$1" = remove ] && [ -d /run/systemd/system ] ; then systemctl --system daemon-reload >/dev/null || truefiif [ "$1" = "purge" ]; then if [ -x "/usr/bin/deb-systemd-helper" ]; then deb-systemd-helper purge 'sshd-keygen.service' >/dev/null || true fifi2.7. config
Section titled “2.7. config”Script run before preinst, before any pre-dependencies are satisfied. Used to prompt the user for configuration decisions.
Example from mariadb-server config:
#!/bin/bash
set -e
# shellcheck source=/dev/null. /usr/share/debconf/confmodule
if [ -n "$DEBIAN_SCRIPT_DEBUG" ]then set -v -x; DEBIAN_SCRIPT_TRACE=1fi
${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*" 1>&2}
# Beware that there are two ypwhich one of them needs the 2>/dev/null!if test -n "$(command -v ypwhich 2>/dev/null)" && ypwhich > /dev/null 2>&1then db_input high mariadb-server/nis_warning || true db_gofi2.8. templates
Section titled “2.8. templates”Contains templates for user prompts (used with debconf).
Excerpt from openssh-server templates:
Template: openssh-server/password-authenticationType: booleanDefault: trueDescription: Allow password authentication? By default, the SSH server will allow authenticating using a password. You may want to change this if all users on this system authenticate using a stronger authentication method, such as public keys.2.9. md5sums
Section titled “2.9. md5sums”MD5 hashes of package files for integrity verification.
Excerpt from openssh-server md5sums:
4b50d38888a7c2093ea4928dca327287 usr/lib/openssh/ssh-session-cleanup36b4eaabb3472c543e7e9e7c0c80a014 usr/lib/openssh/sshd-auth269cc6f51c160b60b0f714ee2f1f434d usr/lib/openssh/sshd-session8ec138e332aa1fbc3f081c17e81b62f9 usr/lib/systemd/system/rescue-ssh.target0cbed0c937f53a8d3e9372faa6c23e4c usr/lib/systemd/system/ssh.service144e44ecb5dfaa27337b7e5d6348e9fa usr/lib/systemd/system/ssh.socketa09b0c24335b507ec5c9f100f9ba3b15 usr/lib/systemd/system/sshd-keygen.service09582565e7d6ab3f1f15293d0bc9ab97 usr/lib/systemd/system/sshd@.service3890b397321c96e14ef5627d1ea251d0 usr/lib/sysusers.d/openssh-server.conf550889177dce3b36aa2717e88c9bf7e1 usr/lib/tmpfiles.d/openssh-server.conf283a135db1d5bf7fbe7483fe56269dfc usr/sbin/sshdf410eed89eecfb2bcf29440141a49d4f usr/share/apport/package-hooks/openssh-server.py77862d01cdeb1232790155687382fdad usr/share/doc/openssh-client/examples/ssh-session-cleanup.service931ec2a78f8e7e9b8971416497aecc5b usr/share/lintian/overrides/openssh-server58d232b99ab4d6c8fde0c12a98b8841c usr/share/man/man5/moduli.5.gz18f27d1ef2de5f84a601ffefa20568d2 usr/share/man/man5/sshd_config.5.gz5a9a2a3ffa3b98bb9bfb77f057fada68 usr/share/man/man8/sshd.8.gz0382ac8785a147d5a10f3fa0cdfd8eb5 usr/share/openssh/sshd_config07e75bae4aeca2bbf7bb42ba6af88a7d usr/share/openssh/sshd_config.md5sumd41d8cd98f00b204e9800998ecf8427e usr/share/runit/meta/ssh/installed2.10. triggers
Section titled “2.10. triggers”Declares package relationships with system triggers.
Trigger directives:
interest <trigger>– Package is interested in the trigger.interest-await <trigger>– Puts triggering package in “awaited” state.interest-noawait <trigger>– Does not await the trigger.activate <trigger>– Any change activates the trigger.activate-await <trigger>– Only puts package in “awaited” state.activate-noawait <trigger>– Never puts package in “awaited” state.
Example from mariadb-server triggers:
interest-noawait /etc/mysqlinterest-noawait /etc/systemd/system/mariadb.service.d2.11. symbols and shlibs
Section titled “2.11. symbols and shlibs”Contain shared library information.
Reference:
https://man7.org/linux/man-pages/man5/deb-symbols.5.html
https://man7.org/linux/man-pages/man5/deb-shlibs.5.html
Example from slapd shlibs:
libslapi 2 slapd (>= 2.6.10+dfsg)3. Package Data Files
Section titled “3. Package Data Files”3.1. Basics
Section titled “3.1. Basics”Data files reside in the data.tar.zst archive. This archive contains files organized in their corresponding directory structure. During installation, each file is extracted to its appropriate location in the filesystem.
Example: Directory structure of the openssh-server package (Ubuntu 22.04 LTS):
├── etc│ ├── default│ │ └── ssh│ ├── init.d│ │ └── ssh│ ├── pam.d│ │ └── sshd│ ├── ssh│ │ ├── moduli│ │ └── sshd_config.d│ └── ufw│ └── applications.d│ └── openssh-server├── lib│ └── systemd│ └── system│ ├── rescue-ssh.target│ ├── ssh.service│ ├── ssh@.service│ └── ssh.socket└── usr ├── lib │ └── openssh │ └── ssh-session-cleanup ├── sbin │ └── sshd └── share ├── apport │ └── package-hooks │ └── openssh-server.py ├── doc │ ├── openssh-client │ │ └── examples │ │ └── ssh-session-cleanup.service │ └── openssh-server -> openssh-client ├── man │ ├── man5 │ │ ├── authorized_keys.5.gz -> ../man8/sshd.8.gz │ │ ├── moduli.5.gz │ │ └── sshd_config.5.gz │ └── man8 │ └── sshd.8.gz └── openssh ├── sshd_config └── sshd_config.md5sum3.2. File Locations
Section titled “3.2. File Locations”Data files include binaries, static files, dynamic files, configurations, and more. Each file must be placed in an appropriate directory.
Common locations:
- Architecture-independent, application-specific files →
/usr/share - User-specific configuration files →
~/.packagename(file or directory) - System-wide configuration files →
/etc/packagename - Systemd unit files →
/lib/systemd/system - Log files →
/var/log/packagename - Cron jobs (depending on frequency):
/etc/cron.hourly/etc/cron.daily/etc/cron.weekly/etc/cron.monthly- For custom schedules →
/etc/cron.d
4. Package Installation Process
Section titled “4. Package Installation Process”The installation process of a .deb package involves multiple steps, including script execution and dependency management. Below is a simplified overview of the flow:
If there is a previous version of the package: Call for the old package: "old-prerm upgrade new-version" If return status is not 0: Call for the current package: "new-prerm failed-upgrade old-version new-version" If return status is not 0: Call for the old package: "old-postinst abort-upgrade new-version" If return status is 0: Upgrade unsuccessful, old version remains functional Exit Else: Upgrade unsuccessful, old version is broken Exit
If there are conflicting and/or broken packages: For each conflicting/broken package: # Unconfigure them deconfigured's-prerm deconfigure in-favour package-being-installed version If return status is not 0: deconfigured's-postinst abort-deconfigure in-favour \ package-being-installed-but-failed version # Remove them conflictor's-prerm remove in-favour package new-version If return status is not 0: conflictor's-postinst abort-remove in-favour package new-version If any packages depend on the conflicting/broken packages: For each dependent package: # Unconfigure them deconfigured's-prerm deconfigure in-favour package-being-installed \ version removing conflicting-package version If return status is not 0: deconfigured's-postinst abort-deconfigure in-favour \ package-being-installed-but-failed version \ removing conflicting-package version # Prepare to remove conflicting packages conflictor's-prerm remove in-favour package new-version If return status is not 0: conflictor's-postinst abort-remove in-favour package new-version
# Run the preinst script of the new packageIf the package is being upgraded: new-preinst upgrade old-version new-version If return status is not 0: new-postrm abort-upgrade old-version new-version If return status is 0: Upgrade unsuccessful, old version remains functional Exit Else: Upgrade unsuccessful, old version is broken ExitElse if the package is new but old configuration files exist: new-preinst install old-version new-version If return status is not 0: new-postrm abort-install old-version new-version Installation unsuccessful ExitElse: # Fresh installation with no existing configuration files new-preinst install If return status is not 0: new-postrm abort-install Installation unsuccessful Exit
Unpack all files from the data archive# A package cannot overwrite another package's files unless specified with "Replaces"
If the package is being upgraded: old-postrm upgrade new-version If return condition is not 0: new-postrm failed-upgrade old-version new-version If return condition is 0: Upgrade failed, old version remains functional Exit Else: # Upgrade failed; attempt to recover old-preinst abort-upgrade new-version If return condition is 0: new-postrm abort-upgrade old-version new-version If return condition is 0: old-postinst abort-upgrade new-version Upgrade failed, old version remains functional Exit Else: Upgrade failed, old version broken Exit Else: Upgrade failed, old version broken Exit
Files present in the old version but not in the new version are removedThe new file list replaces the old oneNew maintenance scripts replace the old onesFor each package whose files were overwritten and is no longer required for dependencies: # Remove the overwritten package disappearer's-postrm disappear overwriter overwriter-version Remove package scripts Update package status to "Not-Installed"If any files in the new package are also listed in other packages: Remove themRemove temporary and backup files created during installationSet the package status to "Unpacked"If there were conflicting packages: Remove them5. Package Removal/Purge Process
Section titled “5. Package Removal/Purge Process”The removal or purging of a package follows a structured sequence:
Run: prerm removeIf return status is not 0: If failure is due to conflict: Run: conflictor's-postinst abort-remove in-favour package new-version Else: Run: postinst abort-remove If return status is 0: Removal aborted; package remains installed Exit Else: Removal aborted; package is in a broken state Exit
Remove all package files (except configuration files listed in conffiles)Remove all package maintenance scripts except postrm
If purging (not just removing): Remove configuration files and temporary/backup files created during installation Run: postrm purge Remove the package's file list from the system database6. .deb Package Preparation Checklist
Section titled “6. .deb Package Preparation Checklist”6.1. Create File and Folder Structure for the Package
Section titled “6.1. Create File and Folder Structure for the Package”A minimal directory structure for a package includes:
ProgramName-Version/├── DEBIAN/│ └── control└── usr/ └── bin/ └── ProgramNameAdditional files and directories you may need:
ProgramName-Version/├── DEBIAN/│ ├── control│ ├── preinst│ ├── postinst│ ├── prerm│ └── postrm├── etc/│ ├── ProgramName.conf│ └── ProgramName/├── usr/│ ├── share/│ │ ├── applications/│ │ │ └── ProgramName.desktop│ │ └── ProgramName/│ │ └── ProgramFiles│ └── bin/│ └── ProgramName└── (other directories as needed)6.2. Copy Files
Section titled “6.2. Copy Files”Place your program executable, control files, scripts, and other resources into the appropriate locations:
/usr/share/ProgramName/: Use this directory if your program consists of multiple files./etc/or/etc/ProgramName/: For configuration files (single file or a directory of configs)./usr/share/applications/: For desktop launcher files (.desktopentries).
6.3. Preparing and Building The Package
Section titled “6.3. Preparing and Building The Package”Once all files are in place, ensure executable files have the correct permissions:
chmod a+x ProgramName-Version/usr/bin/ProgramNameThen build the package using dpkg-deb:
dpkg-deb -Z xz --build --root-owner-group ProgramName-VersionThe resulting .deb file (e.g., ProgramName-Version.deb) is your ready-to-use package.
7. A Very Simple Package
Section titled “7. A Very Simple Package”We will now create a very simple package named “distro”. This package contains a Python script that displays the Unix distribution name and version information.
The package includes only the executable script and the control file—no configuration files or maintenance scripts are required.
7.1. Create Folder Structure
Section titled “7.1. Create Folder Structure”First, create a packages directory in your home folder to organize your work:
mkdir ~/packagesPackage naming follows the format: packagename_version-revision_architecture.deb. Create a folder for our package:
mkdir ~/packages/distro_1.0.0-1_allCreate subdirectories for the executable and control files:
mkdir -p ~/packages/distro_1.0.0-1_all/usr/binmkdir ~/packages/distro_1.0.0-1_all/DEBIAN7.2. Create the Executable File
Section titled “7.2. Create the Executable File”Create the Python script:
nano ~/packages/distro_1.0.0-1_all/usr/bin/distroAdd the following content:
#!/usr/bin/env python3def distro_version(): d = {} try: with open("/etc/os-release") as f: for line in f: line = line.replace('"', '') line = line.replace('\n', '') if line == "": continue (key, val) = line.split("=") d[(key)] = val distro = d["NAME"] version = d["VERSION_ID"] except: distro = "Other" version = "Other" return distro, versiondistro, version = distro_version()print(distro, version)Make the script executable:
chmod +x ~/packages/distro_1.0.0-1_all/usr/bin/distro7.3. Create the Control File
Section titled “7.3. Create the Control File”Create the control file:
nano ~/packages/distro_1.0.0-1_all/DEBIAN/controlAdd the following content:
Package: distroVersion: 1.0.0-1Architecture: allMaintainer: Exforge <exforge@x386.org>Depends: python3Homepage: https://github.com/enatsek/Description: Prints Linux Distribution and Name A python script to print linux distro and version of the distro. I do not know what it does on other OSs.7.4. Build the package
Section titled “7.4. Build the package”Navigate to the packages directory and build the package:
cd ~/packagesdpkg-deb -Z xz --build --root-owner-group distro_1.0.0-1_allYour package, distro_1.0.0-1_all.deb, is now located in the ~/packages folder.
Note: dpkg-deb typically uses zst compression, but we override it with the -Z xz flag for broader compatibility. Packages compressed with zst (default in Ubuntu 22.04) may not work on older Debian distributions.
8. A More Complex Package: Watchbox
Section titled “8. A More Complex Package: Watchbox”Our next package, watchbox, is more complex. It is a Python script designed to run as a systemd service.
The package must:
- Install as a systemd service
- Enable and start the service after installation
- Stop the service before removal or upgrade
Required control files: control, conffiles, postinst, and prerm.
Data files: the script itself, a configuration file, and a systemd unit file.
8.0. About Watchbox
Section titled “8.0. About Watchbox”Watchbox is a demonstration service that periodically checks network connectivity for hosts, web pages, files, or services.
It consists of:
watchbox: the main applicationwatchbox.conf: configuration filewatchbox.service: systemd service unit file
For more details, see: https://github.com/enatsek/watchbox
8.1. Download Watchbox Files
Section titled “8.1. Download Watchbox Files”Download the necessary files from GitHub to a temporary location:
wget https://raw.githubusercontent.com/enatsek/watchbox/main/watchbox \ -P /tmpwget https://raw.githubusercontent.com/enatsek/watchbox/main/watchbox.conf \ -P /tmpwget https://raw.githubusercontent.com/enatsek/watchbox/main/watchbox.service \ -P /tmp8.2. Folder Structure
Section titled “8.2. Folder Structure”The package will have the following structure:
/packages/~/packages/watchbox_0.9-1_all/├── DEBIAN/│ ├── control│ ├── conffiles│ ├── postinst│ └── prerm├── etc/│ └── watchbox.conf├── lib/│ └── systemd/│ └── system/│ └── watchbox.service└── usr/ └── bin/ └── watchbox8.3. Create Folder Structure
Section titled “8.3. Create Folder Structure”Create the directories:
mkdir -p ~/packages/watchbox_0.9-1_all/DEBIAN/mkdir -p ~/packages/watchbox_0.9-1_all/etcmkdir -p ~/packages/watchbox_0.9-1_all/lib/systemd/system/mkdir -p ~/packages/watchbox_0.9-1_all/usr/bin/8.4. Copy Watchbox Files to Their Locations
Section titled “8.4. Copy Watchbox Files to Their Locations”cp /tmp/watchbox.conf ~/packages/watchbox_0.9-1_all/etc/cp /tmp/watchbox.service ~/packages/watchbox_0.9-1_all/lib/systemd/system/cp /tmp/watchbox -p ~/packages/watchbox_0.9-1_all/usr/bin/Make the script executable:
chmod +x ~/packages/watchbox_0.9-1_all/usr/bin/watchbox8.5. Create Control Files and Maintenance Scripts
Section titled “8.5. Create Control Files and Maintenance Scripts”Control File (control):
nano ~/packages/watchbox_0.9-1_all/DEBIAN/controlAdd the following content:
Package: watchboxVersion: 0.9-1Architecture: amd64Maintainer: Exforge <exforge@x386.org>Homepage: https://github.com/enatsek/watchbox/Depends: python3, python3-systemdDescription: Automated Service Checking Tool WatchBox (AKA watchbox) is planned to be a Systemd service for starting at the power-up and making periodic checks. Currently it has 6 types of checks: - IPPing: Checks if an IP address or hostname can be pinged - IPPort: Checks if an IP address or hostname can be connected through a TCP Port - Webpage: Checks if a webpage exists - WebpageContent: Checks if a webpage has a content - LocalPath: Checks if a path exists - LocalService: Checks if a systemd service is active Check status results can be collected to systemd journal, text file, and/or sqlite db file. Configuration file watchbox.conf is expected to be in /etc directory. It is well documented inside. Disclaimer: This program is far from being optimized, so it is not adviced to use it on production environments.Configuration File List (conffiles):
nano ~/packages/watchbox_0.9-1_all/DEBIAN/conffilesAdd:
/etc/watchbox.confPost-Installation Script (postinst):
nano ~/packages/watchbox_0.9-1_all/DEBIAN/postinstAdd:
#!/bin/bashsystemctl enable watchboxsystemctl start watchboxMake it executable:
chmod +x ~/packages/watchbox_0.9-1_all/DEBIAN/postinstPre-Removal Script (prerm):
nano ~/packages/watchbox_0.9-1_all/DEBIAN/prermFill as below (Stops watchbox service)
#!/bin/bashsystemctl stop watchboxMake it executable:
chmod +x ~/packages/watchbox_0.9-1_all/DEBIAN/prerm8.6. Build the Package
Section titled “8.6. Build the Package”Navigate to the packages directory and build:
cd ~/packagesdpkg-deb -Z xz --build --root-owner-group watchbox_0.9-1_allYour package, watchbox_0.9-1_all.deb, is now ready in the ~/packages folder.