
If you have ever compiled software from a .tar.gz or .tgz source tarball on Ubuntu, you already know the pain that follows. You run ./configure, make, and make install, the software works fine, and then one day you need to remove it. There is no clean uninstall path, no version tracking, and APT has no idea the software even exists on your system. That is the core problem this Linux server tutorial solves.
Creating an Ubuntu DEB package from source code gives you a structured, reversible, and APT-managed installation. You can install it, upgrade it, share it with teammates, or remove it completely without leaving orphaned files scattered across your filesystem.
In this guide, you will learn two practical methods to configure an Ubuntu DEB package from a source tarball: a quick approach using checkinstall for local use, and a full approach using dpkg-buildpackage for distribution-quality packaging. Both methods are tested on Ubuntu 20.04, 22.04, and 24.04 LTS.
By the end, you will have a working .deb file built from a real upstream source tarball, validated, installed, and cleanly removable through Ubuntu’s native package management system. This is the workflow senior sysadmins use, and it is not as complicated as the Debian documentation makes it look.
What Is an Ubuntu DEB Package and Why Build One?
A .deb file is the native binary package format used by Debian and Ubuntu. Internally, it is a Unix archive containing two tar sub-archives: one holding control metadata and one holding the actual program files. These archives can be compressed using gzip, bzip2, lzma, or xz.
When you install a .deb file with dpkg or APT, the package manager registers every file it places on your system. That registration is what gives you clean removal, version tracking, and dependency resolution.
The Problem with make install
Running make install directly from source drops files into /usr/local/bin, /usr/local/lib, and other directories with no record of what was placed where.
You cannot upgrade cleanly. You cannot remove cleanly. And if two packages install conflicting files, you have no tooling to detect or resolve that conflict. This is why packaging matters, even for software you only use internally.
checkinstall vs. dpkg-buildpackage
Both tools produce a .deb file, but they serve different purposes. Here is a direct comparison:
| Feature | checkinstall | dpkg-buildpackage |
|---|---|---|
| Setup complexity | Low | Medium to High |
| Output quality | Basic .deb | Policy-compliant .deb |
| Best for | Local personal use | Distribution, PPAs, CI/CD |
| Lintian-clean output | No | Yes, with tuning |
| Build time | Fast | Slightly longer |
Use checkinstall when you want a quick Ubuntu DEB package setup on a single machine. Use dpkg-buildpackage when you need a package that meets Debian policy standards for sharing or publishing.
Prerequisites
Before you begin this Linux server tutorial, confirm the following:
System requirements:
- Ubuntu 20.04 LTS, 22.04 LTS, or 24.04 LTS (desktop or server)
- A non-root user account with
sudoprivileges - Active internet connection to download packages and source tarballs
- At least 500 MB of free disk space in your build directory
Knowledge requirements:
- Comfort with running commands in a Linux terminal
- Basic understanding of
tarextraction and file navigation - Familiarity with a terminal text editor such as
nanoorvim
Tools you will install in Step 1:
build-essential(gcc, g++, make)checkinstalldevscripts,dh-make,dpkg-devlintian
If you are building packages you intend to distribute or upload to a Launchpad PPA, consider running all of this inside a clean VM or Docker container to avoid polluting your host system’s build environment.
Step 1: Update Your System and Install Build Dependencies
Every good Ubuntu DEB package setup starts with a fully updated system. Running builds on a partially updated system is one of the most common sources of subtle dependency errors that are hard to debug.
Update your package index and upgrade installed packages
sudo apt update && sudo apt upgrade -y
This command refreshes your local package list from Ubuntu’s repositories and upgrades any outdated packages. Always do this before installing new development tools.
Install all required build tools in one command
sudo apt install build-essential automake autoconf libtool \
pkg-config checkinstall devscripts dh-make dpkg-dev lintian -y
Here is what each tool does:
- build-essential: Installs
gcc,g++,make, and standard C library headers — the minimum toolchain for compiling C/C++ source code - checkinstall: Intercepts
make installand generates a.debfile instead of writing files directly to the filesystem - devscripts: Provides helper scripts like
dch(changelog editor) anddebuild(high-level build wrapper) - dh-make: Generates a
debian/directory skeleton inside your source tree - dpkg-dev: Provides
dpkg-buildpackageand other core packaging utilities - lintian: Static analysis tool that checks your package against official Debian policy rules
Verify the key tools installed correctly
dpkg-buildpackage --version
lintian --version
checkinstall --version
You should see version strings for each tool. If any command returns “not found,” re-run the install command above.
Step 2: Download and Prepare Your Source Tarball
For this tutorial, you will use Dante, a free SOCKS server and client, as the example package. Dante is a real-world C project with a standard GNU build system, making it an ideal example.
Download the upstream source tarball
mkdir -p ~/build && cd ~/build
wget http://www.inet.no/dante/files/dante-1.3.2.tar.gz
wget downloads the tarball into your ~/build directory. The -p flag in mkdir ensures no error if the directory already exists.
Extract the tarball
tar zxvf dante-1.3.2.tar.gz
z: decompress using gzipx: extract the archivev: verbose output (shows each file being extracted)f: specifies the filename
This creates a directory named dante-1.3.2/.
Verify the directory name format
The directory name must follow the name-version format in lowercase. Debian packaging tools parse this directory name to determine the package name and version automatically.
ls ~/build/
Expected output:
dante-1.3.2 dante-1.3.2.tar.gz
Create the orig tarball for dpkg-buildpackage (Method 2 only)
cp dante-1.3.2.tar.gz dante_1.3.2.orig.tar.gz
Note the underscore between name and version in the copy. This naming convention (name_version.orig.tar.gz) is required by dpkg-buildpackage. The hyphen format is for the directory; the underscore format is for the orig tarball.
Navigate into the source directory
cd dante-1.3.2
All subsequent commands run from inside this directory.
Step 3: Configure and Build the Source Code
This step is identical for both Method 1 (checkinstall) and Method 2 (dpkg-buildpackage). You are compiling the software before packaging it.
Run the configure script
./configure
The ./configure script checks your system for required libraries and compiler features, then generates a Makefile tailored to your environment. If it fails, it will tell you exactly which library or header is missing. Install that dependency with apt and run ./configure again.
Compile the source code
make
make reads the generated Makefile and compiles the source files into binary executables. On a modern multi-core machine, speed up this step with:
make -j$(nproc)
$(nproc) outputs the number of available CPU cores, allowing make to parallelize the compilation. A successful build ends without any Error messages in the output.
Step 4: Method 1 — Quick DEB Packaging with checkinstall
This is the fastest way to create a functional Ubuntu DEB package from source. It requires no manual metadata configuration and works well for local deployments and internal servers.
Run checkinstall instead of make install
sudo checkinstall
Instead of writing files directly to your system like make install would, checkinstall monitors the installation and wraps all placed files into a .deb archive.
Respond to the interactive prompts
checkinstall will ask you several questions. Here are the recommended responses:
- Package name:
dante(lowercase, no spaces) - Version:
1.3.2 - Maintainer: Your name and email
- Summary: A short one-line description
After you confirm, checkinstall generates a file like dante_1.3.2-1_amd64.deb in your current directory.
Install and verify the generated package
sudo dpkg -i dante_1.3.2-1_amd64.deb
dpkg -s dante
dpkg -s prints the package status, version, architecture, and description — confirming that APT now tracks this installation.
Remove the package cleanly
sudo apt remove dante
Using apt remove instead of dpkg -r is preferred because APT handles any dependency cleanup automatically.
Step 5: Method 2 — Full DEB Packaging with dpkg-buildpackage
This method produces a policy-compliant .deb package suitable for Launchpad PPAs, Debian repositories, or sharing across teams. It takes more setup but gives you a properly structured package that passes lintian checks.
Generate the debian/ directory skeleton
From inside ~/build/dante-1.3.2/, run:
dh_make -s --createorig -e [email protected]
-s: creates a single binary package--createorig: generates the.orig.tar.gzfile automatically-e: sets your email address in the package metadata
This creates a debian/ directory with the following structure:
debian/
├── changelog
├── compat
├── control
├── copyright
├── rules
└── source/
└── format
Configure debian/control — the package metadata file
Open the control file:
nano debian/control
Fill in or edit these key fields:
Source: dante
Section: net
Priority: optional
Maintainer: Your Name <[email protected]>
Build-Depends: debhelper-compat (= 13)
Standards-Version: 4.6.0
Package: dante
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Free SOCKS server implementation
Dante is a free implementation of the SOCKS proxy protocol,
version 4 and version 5. It can be used as a firewall bypass tool.
Key field explanations:
- Architecture: any means the package compiles to native code and must be built per-architecture. Use
allfor scripts or architecture-independent data - Depends: ${shlibs:Depends} is a variable that
dpkg-buildpackageresolves automatically by scanning the compiled binaries for shared library links - The long Description must be indented with one space per line
Write debian/rules — the build instructions
Open the rules file:
nano debian/rules
A minimal modern rules file using the dh helper looks like this:
#!/usr/bin/make -f
%:
dh $@
Make it executable:
chmod +x debian/rules
The dh command is the debhelper sequencer. It automatically runs the correct build, install, and packaging steps in the right order based on the build system it detects.
Update the changelog
Use dch to create a properly formatted changelog entry:
dch -i "Initial packaging of dante from upstream source"
Set the distribution to your Ubuntu release:
dch -r ""
The changelog format is strict. dch handles the formatting automatically so you do not introduce syntax errors.
Set the debhelper compat level
echo "13" > debian/compat
Debhelper compatibility level 13 is the current recommended level for Ubuntu 20.04 and later.
Build the package
cd ~/build
dpkg-buildpackage -us -uc
-us: do not sign the source package (for local builds)-uc: do not sign the.changesfile (for local builds)
A successful build produces these files in ~/build/:
dante_1.3.2-1_amd64.deb— the installable binary packagedante_1.3.2-1.dsc— the source package descriptiondante_1.3.2-1.debian.tar.xz— thedebian/directory as an archivedante_1.3.2-1_amd64.changes— the changes file used for repository uploads
Step 6: Validate Your Package with Lintian
Before you install, share, or publish your Ubuntu DEB package, run it through lintian. This tool checks your package against the official Debian Policy Manual and reports errors and warnings.
lintian ~/build/dante_1.3.2-1_amd64.deb
For more detailed output with tag explanations:
lintian -v --explain-tags ~/build/dante_1.3.2-1_amd64.deb
Errors (E:) must be fixed before distribution. Warnings (W:) are context-dependent but should be reviewed.
Common lintian tags and what they mean:
W: no-copyright-file— yourdebian/copyrightfile is missing or empty. Add a valid license declarationW: description-synopsis-is-duplicated— your short and long description are identical. Write a more detailed long descriptionE: bad-distribution-in-changes-file— the distribution field indebian/changelogdoes not match a valid Ubuntu release name
Step 7: Install, Test, and Remove the Package
With your validated .deb file ready, install it using dpkg:
sudo dpkg -i ~/build/dante_1.3.2-1_amd64.deb
Verify the installation registered correctly with APT:
dpkg -s dante
List every file the package installed on your system:
dpkg -L dante
Test that the binary actually runs:
sockd --version
When you are ready to remove it:
sudo apt remove dante
APT tracks this package just like any other software installed from Ubuntu’s repositories. Clean removal is guaranteed.
Troubleshooting Common Errors
Problem 1: dpkg-buildpackage fails with missing build dependencies
Error message: dpkg-checkbuilddeps: error: Unmet build dependencies
Solution: Add the missing package to Build-Depends in debian/control, then run:
sudo apt-get build-dep .
This command installs all packages listed in Build-Depends automatically.
Problem 2: dh_make fails with a directory naming error
Error message: Could not find a valid package name and version in the directory name
Solution: Your source directory must follow the exact name-version pattern in lowercase:
mv Dante-1.3.2 dante-1.3.2
Rename the directory, then re-run dh_make.
Problem 3: checkinstall fails after make succeeds
Solution: Make sure you ran ./configure and make without errors before running checkinstall. Never skip the make step and jump straight to checkinstall.
Problem 4: lintian reports W: no-copyright-file
Solution: Edit debian/copyright and add a valid license block. For a minimal fix:
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: dante
License: BSD-4-clause
Problem 5: debian/rules not executable
Error message: make: debian/rules: Permission denied
Solution: Run:
chmod +x debian/rules
This makes the rules file executable so dpkg-buildpackage can call it directly as a Makefile.