1. Overview

A Debian package is the simplest and most efficient way of distributing software in Debian-based distributions. It takes care of managing dependencies and provides a good interface for install/upgrade/uninstall operations.

The official way of creating a package involves a lot of steps and processes. In this tutorial, we’ll look at a simpler way of creating these packages. However, the official way of package creation is ideal and recommended for production purposes.

2. Preparing the Files

2.1. Structure of .deb Package

In Debian-based distributions, one of the ways we install applications is by downloading the .deb package file and using dpkg command to install it. This “deb package” is an archive of binaries and configuration files associated with a software application. All the files inside the archive are kept in a specific folder structure.

During installation, in the target machine, the binaries and configuration files go into a similar folder structure from the root folder.

Let’s look at an example to understand more clearly. First, we create a working folder for the build:

bluelake@pacific:~/Documents/deb/build$ mkdir test

Please note the path – we’ll be changing the folder as and when we progress through this exercise.

Then, we prepare files for our application. To keep things simple, let’s have two files:

  • test.sh executable
  • test.conf configuration file

For these files, we’ll keep the contents as below for this exercise:

bluelake@pacific:~/Documents/deb/build/test$ cat test.conf 
NAME=Test
bluelake@pacific:~/Documents/deb/build/test$ cat test.sh
#!/bin/bash
. test.conf
echo "Hi $NAME"

We know that the executable files in Debian are kept in the /bin folder and the configuration files in the /etc folder. Following the convention, we’ll keep the test.sh file in the /bin folder and test.conf in the /etc folder.

Hence, we create two folders, bin and etc, in our current directory:

bluelake@pacific:~/Documents/deb/build/test$ mkdir {bin,etc}
bluelake@pacific:~/Documents/deb/build/test$ ls
bin  etc  test.conf  test.sh

Now, let’s move the executable and configuration files to their respective folders:

bluelake@pacific:~/Documents/deb/build/test$ mv test.conf etc/
bluelake@pacific:~/Documents/deb/build/test$ mv test.sh bin/
bluelake@pacific:~/Documents/deb/build/test$ ls
bin  etc

With this, we’ve placed our files in a folder structure that would be the same once it’s installed in the target machine.

2.3. The control File

Finally, we need a control file that contains the metadata for the package. This is a text file with fields and descriptions. The supported fields are:

  • Package (Required): Name of package
  • Version (Required): Version of the package
  • Maintainer (Required): Name and email of maintainer
  • Architecture (Required): Supported target machine architecture
  • Description (Required): Short description of the package
  • Section: Package classification like admin, database, kernel, utils
  • Priority: Whether the package is optional or required
  • Essential: Whether the package is always required
  • Depends: List other dependent packages like libc6 (>= 2.2.4-4)
  • Homepage: URL of the website associated with the package
  • Package-Type: Indicate the types, like deb or udeb, for example

As mentioned above, some fields are required and others are optional.

Using this information, let’s create a control file with the required fields and some of the recommended fields. This file goes under a folder named DEBIAN:

bluelake@pacific:~/Documents/deb/build/test$ ls
bin  DEBIAN  etc
bluelake@pacific:~/Documents/deb/build/test$ cat DEBIAN/control 
Package: test
Version: 1.0-1
Section: utils
Priority: optional
Architecture: all
Maintainer: Baeldung <[email protected]>
Description: This is a test application
 for packaging

3. Building the Package

With that, we’re ready to build our package. And for that, we can use the dpkg-deb command:

bluelake@pacific:~/Documents/deb/build$ dpkg-deb --root-owner-group --build test
dpkg-deb: building package 'test' in 'test.deb'.
bluelake@pacific:~/Documents/deb/build$ ls
test  test.deb

As we can see, a test.deb file is created successfully. With that, we are almost ready with our package.

4. Verify the Package

4.1. Check Contents

Once the package is created, we can verify if the files are present in it by running the dpkg command:

bluelake@pacific:~/Documents/deb/build$ dpkg -c test.deb 
drwxr-xr-x thinkpalm/thinkpalm 0 2021-12-23 12:09 ./
drwxr-xr-x thinkpalm/thinkpalm 0 2021-12-23 11:30 ./bin/
-rwxr-xr-x thinkpalm/thinkpalm 40 2021-12-23 11:15 ./bin/test.sh
drwxr-xr-x thinkpalm/thinkpalm  0 2021-12-23 11:30 ./etc/
-rw-r--r-- thinkpalm/thinkpalm 10 2021-12-23 11:15 ./etc/test.conf

From the results, we see that it contains the files we have added in their expected paths.

4.2. Linting

In order to further comply with the Debian packaging conventions, we can lint the package. For this, we use the tool called lintian.

Let’s see how we can use this tool:

bluelake@pacific:~/Documents/deb/build$ lintian test.deb 
E: test: debian-changelog-file-missing
E: test: file-in-etc-not-marked-as-conffile etc/test.conf
E: test: no-copyright-file
W: test: script-with-language-extension bin/test.sh
W: test: binary-without-manpage bin/test.sh

After running the command, we can see some errors marked as E and warnings marked as W.

Let’s check how we can fix these, one by one.

Changelog File Missing:

To fix this error, we can add a changelog file:

bluelake@pacific:~/Documents/deb/build$ cat changelog.Debian 
test (1.0-2) stable; urgency=low

  [ Baeldung ]
  * Changes to installer 

 -- Baeldung <[email protected]>  Thu,  23 Dec 2021 11:30:00 +0100 

test (1.0-1) stable; urgency=low

  [ Baeldung ]
  * Wonderful program to print the name ;)

 -- Baeldung <[email protected]>  Thu,  23 Dec 2021 11:00:00 +0100 
bluelake@pacific:~/Documents/deb/build$ gzip --best -n changelog.Debian 
bluelake@pacific:~/Documents/deb/build$ mkdir -p test/usr/share/doc/test
bluelake@pacific:~/Documents/deb/build$ cp changelog.Debian.gz test/usr/share/doc/test/

Marking File as Conf File:

In order to fix this error, we can add a conffile in the DEBIAN folder:

bluelake@pacific:~/Documents/deb/build$ cat test/DEBIAN/conffiles 
/etc/test.conf

Adding Copyright File:

To fix this one, we can add a copyright file:

bluelake@pacific:~/Documents/deb/build$ cat test/usr/share/doc/test/copyright 
test

Copyright: 2020 Baeldung <[email protected]>

2020-12-23

The entire code base may be distributed under the terms of the GNU General
Public License (GPL), which appears immediately below.  Alternatively, all
of the source code as any code derived from that code may instead be
distributed under the GNU Lesser General Public License (LGPL), at the
choice of the distributor. The complete text of the LGPL appears at the
bottom of this file.

See /usr/share/common-licenses/(GPL|LGPL)

Removing Extension for Binary File:

Let’s remove the extension for the script file to fix this error:

bluelake@pacific:~/Documents/deb/build$ mv test/bin/test.sh test/bin/test 

Adding Man Entry for Binary:

Let’s add a man file to fix this error:

bluelake@pacific:~/Documents/deb/build$ cat test.1
.\"                                      Hey, EMACS: -*- nroff -*-
.\" (C) Copyright 2020 Baeldung <[email protected]>
.\"
.TH TEST 1 
.SH NAME
test \- test application
.SH SYNOPSIS
.B test.sh 
.SH DESCRIPTION
The 
.B test 
prints the name.
.SH SEE ALSO
.BR echo (1).
.SH AUTHORS
The
.B test 
script was written by 
Baeldung <[email protected]>
.PP
This document was written by Baeldung <[email protected]> for Debian.

bluelake@pacific:~/Documents/deb/build$ gzip --best -n test.1
bluelake@pacific:~/Documents/deb/build$ ls
build.sh  changelog.Debian.gz  changelog.gz  test  test_1.0-1.deb  test.1.gz
bluelake@pacific:~/Documents/deb/build$ mkdir -p test/usr/share/man/man1
bluelake@pacific:~/Documents/deb/build$ cp test.1.gz test/usr/share/man/man1/

The final folder structure would be:

bluelake@pacific:~/Documents/deb/build$ tree test
test
├── bin
│   └── test
├── DEBIAN
│   ├── conffiles
│   └── control
├── etc
│   └── test.conf
└── usr
    └── share
        ├── doc
        │   └── test
        │       ├── changelog.Debian.gz
        │       └── copyright
        └── man
            └── man1
                └── test.1.gz

9 directories, 7 files

Let’s build this again:

bluelake@pacific:~/Documents/deb/build$ dpkg-deb --root-owner-group --build test
dpkg-deb: building package 'test' in 'test.deb'.
bluelake@pacific:~/Documents/deb/build$ mv test.deb test_1.0-2.deb
bluelake@pacific:~/Documents/deb/build$ lintian test_1.0-2.deb
bluelake@pacific:~/Documents/deb/build$

Now, we can see all the errors and warnings are fixed.

5. Conclusion

In this tutorial, we went through the process of creating a deb package with minimum steps. We hope this will be a good starting point for learning to create Debian packages.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.