apt gets all the attention because it’s what you use day to day, but underneath every single install, upgrade, and removal on a Debian-based system, dpkg is doing the actual work. I didn’t really appreciate how much control dpkg gives you directly until I started troubleshooting a broken package state that apt alone couldn’t untangle. Understanding dpkg properly changed how I debug package problems entirely.
What dpkg Actually Is
dpkg (Debian Package) is the low-level package manager underlying the entire Debian package ecosystem. It’s responsible for the mechanics: unpacking .deb archive contents onto the filesystem, running pre/post install and removal scripts, tracking exactly which files belong to which package, and maintaining the package status database at /var/lib/dpkg/status. What dpkg does not do is resolve dependencies or fetch packages from the network — that’s entirely apt‘s job, layered on top.
I confirmed the version on my system:
dpkg --version
Debian 'dpkg' package management program version 1.22.6 (amd64).
Basic Syntax
dpkg [options] command
Here’s the core command set from dpkg‘s own help output:
-i|--install <.deb file name>... | -R|--recursive <directory>...
--unpack <.deb file name>... | -R|--recursive <directory>...
--configure <package>... | -a|--pending
-r|--remove <package>... | -a|--pending
-P|--purge <package>... | -a|--pending
-V|--verify [<package>...]
--get-selections [<pattern>...]
--set-selections
-s|--status [<package>...]
-p|--print-avail [<package>...]
-L|--listfiles <package>...
-l|--list [<pattern>...]
-S|--search <pattern>...
-C|--audit [<package>...]
Installing a .deb Package Directly
This is the single most common reason to reach for dpkg instead of apt — you have an actual .deb file sitting on disk, downloaded manually or built yourself, and you want to install it directly:
sudo dpkg -i package-name_1.0.0_amd64.deb
The important caveat: dpkg does not resolve or fetch dependencies. If the package needs something not currently installed, dpkg -i will fail with unmet dependency errors. The standard fix is:
sudo dpkg -i package-name_1.0.0_amd64.deb
sudo apt install -f
apt install -f (fix-broken) reads the dependency requirements dpkg already recorded as unmet, then fetches and installs exactly those missing dependencies from the configured repositories, completing the process dpkg alone couldn’t finish.
Removing Packages
sudo dpkg -r package-name
Removes the package but leaves configuration files in place, matching apt remove‘s behavior underneath.
sudo dpkg -P package-name
Purges the package entirely, including configuration files — matching apt purge.
Listing Installed Packages
dpkg -l
I tested this directly and it produces a table with status flags, package name, version, architecture, and description:
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-====================================-====================================-============-================================================================================
ii adduser 3.137ubuntu1 all add and remove users and groups
ii adwaita-icon-theme 46.0-1 all default icon theme of GNOME
ii apt 2.8.3 amd64 commandline package manager
The ii prefix means “installed and configured correctly” — that first character is the desired state (i for install), and the second is the actual current state (i for installed). Other combinations you’ll encounter: rc means removed but configuration files remain, un means unknown/not installed, and anything with uppercase letters (like iF or iH) signals a broken or incomplete state that needs attention.
You can filter with a pattern:
dpkg -l "nginx*"
Checking Package Status
dpkg -s nginx
Shows detailed status information for a specific package — installed version, dependencies, configuration file status, and the maintainer scripts’ last known state. I use this constantly when troubleshooting a package that apt reports as broken, since it shows the raw status flags directly.
Listing Files Owned by a Package
dpkg -L nginx
Lists every file the package installed onto the filesystem — binaries, config files, documentation, everything. Invaluable when you need to know exactly what a package touched, especially before manually removing something or investigating unexpected file changes.
Finding Which Package Owns a File
dpkg -S /usr/sbin/nginx
This is one of the commands I reach for constantly during troubleshooting — given a file path on disk, it tells you which installed package is responsible for it. Extremely useful when you find a mystery binary or config file and need to understand its origin.
dpkg -S /etc/nginx/nginx.conf
Verifying Package Integrity
sudo dpkg -V nginx
-V (--verify) checks the current state of a package’s installed files against the checksums recorded at install time, flagging anything that’s been modified since — extremely useful for detecting configuration drift or, in a security context, unauthorized file tampering.
Auditing for Broken Packages
dpkg -C
-C (--audit) scans the entire system for packages left in a broken, partially-installed, or otherwise inconsistent state, and reports what it finds along with suggested next steps.
Inspecting a .deb File Without Installing It
Before installing an unfamiliar .deb file, I always inspect it first:
dpkg -I package-name_1.0.0_amd64.deb
-I (--info) shows the package’s metadata — name, version, dependencies, maintainer, description — without touching the system at all.
dpkg -c package-name_1.0.0_amd64.deb
-c (--contents) lists every file that would be installed, again without actually installing anything. I use this combination religiously when working with a .deb from a source I don’t fully trust yet, or just to sanity-check a locally built package before deploying it.
Extracting Contents from a .deb File Manually
dpkg-deb -x package-name_1.0.0_amd64.deb /tmp/extracted/
This unpacks the actual file contents into a directory without running any install scripts or registering it with the package database at all — useful for inspecting file contents in isolation, or extracting a single file from a package without a full install.
dpkg-deb -e package-name_1.0.0_amd64.deb /tmp/control/
Extracts just the control information (maintainer scripts, dependency metadata) separately from the actual file payload.
Package Selections: Backup and Restore
One of my favorite lesser-known dpkg features is the ability to snapshot exactly what’s installed on a system, then reproduce that same state elsewhere:
dpkg --get-selections > my-packages-backup.txt
This dumps every package and its current selection state (install, deinstall, purge, hold) to a text file. To restore this state on another machine (after ensuring the same repositories are configured):
sudo dpkg --set-selections < my-packages-backup.txt
sudo apt-get dselect-upgrade
I’ve used this exact pattern when provisioning a new server that needed to match an existing one’s package set closely, well before configuration management tools like Ansible entered the picture for that purpose.
Configuring Pending Packages
Sometimes a package gets unpacked but its configuration step doesn’t complete (often due to an interrupted install):
sudo dpkg --configure -a
The -a (--pending) flag tells dpkg to go through and configure every package left in a pending state. This is one of the first commands I run when a system is reporting broken packages after an interrupted upgrade or a killed apt process.
Handling a Broken dpkg Lock
If dpkg was interrupted mid-operation (power loss, killed process), you might encounter a stale lock:
E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a'
Following that exact suggestion is almost always the correct fix. If a lock file genuinely needs manual removal (verify no dpkg/apt process is actually still running first):
ps aux | grep -i dpkg
sudo rm /var/lib/dpkg/lock-frontend
sudo rm /var/lib/dpkg/lock
sudo dpkg --configure -a
I only remove lock files after confirming no process is genuinely holding them — removing a lock while a process is legitimately running risks real database corruption.
Reconfiguring an Already-Installed Package
sudo dpkg-reconfigure tzdata
dpkg-reconfigure re-runs a package’s configuration scripts (the Debconf prompts), useful for revisiting settings chosen during initial install without a full reinstall — timezone, locale, and similar system-level settings are common targets for this.
Building Your Own .deb Package
For completeness, since it’s directly related: dpkg-deb --build lets you package a prepared directory structure into an installable .deb:
dpkg-deb --build mypackage-1.0/
This expects a specific directory layout, particularly a DEBIAN/control file describing the package metadata. I use this when packaging small internal tools for consistent, trackable deployment across a fleet of servers, rather than relying on raw make install (as discussed in my source-building guide).
Real-World Troubleshooting Workflow
Here’s the sequence I actually follow when a system reports package problems:
# 1. Check for anything broken
dpkg -C
# 2. Try to configure any pending packages
sudo dpkg --configure -a
# 3. Let apt attempt to resolve broken dependencies
sudo apt install -f
# 4. If a specific package is suspect, check its file integrity
sudo dpkg -V suspect-package
# 5. Check what files it owns, in case manual cleanup is needed
dpkg -L suspect-package
Comparison with apt
To be very direct about the division of labor: dpkg installs .deb files you already have and manages the local package database; it has no concept of repositories, downloading, or dependency resolution beyond reporting what’s missing. apt wraps dpkg, adding repository awareness, network fetching, and full dependency graph resolution. In practice, I use apt for essentially all routine work, and drop down to dpkg specifically when I have a .deb file in hand outside of a repository, or when I’m debugging a package database inconsistency apt isn’t giving me enough visibility into.
Security Implications
.deb packages run arbitrary maintainer scripts as root during install, exactly like apt-installed packages do, since apt is calling dpkg underneath either way. Only install .deb files from sources you trust. When inspecting an unfamiliar .deb before installing, I always check its control scripts specifically:
dpkg-deb -e suspicious-package.deb /tmp/control-check/
cat /tmp/control-check/postinst 2>/dev/null
cat /tmp/control-check/preinst 2>/dev/null
Reading these scripts before installing tells you exactly what will execute with root privileges during the install process.
Understanding dpkg’s Package Database Files
Everything dpkg tracks locally lives under /var/lib/dpkg/. Understanding this layout has helped me more than once when troubleshooting a genuinely corrupted package state:
/var/lib/dpkg/status— the master database of every package’s current status, version, dependencies, and configuration state. This single file is whatdpkg -landdpkg -sare actually reading from./var/lib/dpkg/info/— contains per-package files: the list of files it owns (.list), and its maintainer scripts (.postinst,.prerm,.postrm,.preinst)./var/lib/dpkg/available— historically tracked available packages fromdselect; largely superseded byapt‘s own separate caching mechanism today.
I’ve occasionally had to hand-inspect /var/lib/dpkg/status directly (with extreme care, and always backed up first) when a package entry became corrupted in a way that normal dpkg/apt commands couldn’t cleanly recover from — this is very much a last-resort move, not a routine one, since manually editing this file incorrectly can make the package database inconsistent in ways that are much harder to fix than the original problem.
sudo cp /var/lib/dpkg/status /var/lib/dpkg/status.bak
I take this backup habitually before any manual intervention in this territory, even when I’m confident about what I’m doing.
Downgrading a Package with dpkg
Occasionally I need to roll back to a specific older version of a package I’ve kept a .deb copy of, or pulled from an archive:
sudo dpkg -i old-package-version_1.0.0_amd64.deb
dpkg doesn’t inherently object to installing an older version over a newer one — it just does it, unlike some package managers that treat downgrades as a special, blocked operation by default. This makes dpkg genuinely useful for emergency rollbacks when a newer version has introduced a regression, provided you kept the old .deb file around (which is exactly why I keep a small local archive of previously-installed .deb files for critical services, rather than relying solely on apt‘s cache, which gets cleaned periodically).
Comparing Package Versions Programmatically
dpkg --compare-versions 1.2.0 lt 1.3.0 && echo "1.2.0 is older"
This is genuinely useful inside shell scripts that need to make version-aware decisions — for example, only applying a specific workaround or configuration tweak if the installed package version falls below some known-problematic threshold.
INSTALLED=$(dpkg-query -W -f='${Version}' nginx)
if dpkg --compare-versions "$INSTALLED" lt "1.24.0"; then
echo "nginx version is older than required, please upgrade"
fi
Compatibility Notes
dpkg is the foundational package tool for all Debian-derived distributions — Debian itself, Ubuntu and its many derivatives (Linux Mint, Pop!_OS, elementary OS), and embedded distributions like Raspberry Pi OS. RPM-based distributions (RHEL, Fedora, CentOS, openSUSE) use rpm as their equivalent low-level tool, with dnf/yum/zypper playing the role apt plays here.
Summary
dpkg is the engine underneath every apt operation, and understanding it directly gives you a level of visibility and control that becomes essential once you’re troubleshooting genuinely broken package states rather than just doing routine installs. The commands I reach for most — -l to list, -s for status, -L to see owned files, -S to find an owning package, and --configure -a to recover from interrupted installs — cover the vast majority of real troubleshooting scenarios you’ll encounter managing Debian-based systems over time.
References
man 1 dpkgman 1 dpkg-deb- Debian Policy Manual, package format and control file specification (debian.org/doc/debian-policy/)
- Debian Wiki: dpkg (wiki.debian.org/dpkg)
