dpkg

Install and inspect Debian packages directly

Updated 2026-08-18

dpkg is the program that installs Debian packages. It unpacks a .deb, runs its maintainer scripts, records which files it owns, and remembers what state each package is in. Everything apt does ends in a dpkg run.

The difference explains most of what is frustrating about dpkg used alone: dpkg knows about packages, and apt knows about repositories. dpkg works with one file you already have. It cannot download anything, does not know what a repository is, and, the important one, will not resolve dependencies. It checks them, refuses to configure a package whose dependencies are missing, and tells you what was missing. Finding and installing those is apt's job.

That single fact is behind the most common dpkg experience: dpkg -i something.deb fails with a wall of dependency problems, and the fix is to let apt clean up after it.

What dpkg is better at

Given that, most installing is better done through apt. What dpkg is for:

  • Asking what is installed, and in what state. dpkg -l, dpkg -s and dpkg -L answer from the local database with no network involved at all, which also makes them the tools that still work on a machine whose networking or apt configuration is broken. Listing what is installed is the tour of that question.
  • Finding which package owns a file. dpkg -S /path is the fastest way to identify an unfamiliar file on a system, and it only sees files that are already there. Which package provides a file covers the case where the file is still in the archive.
  • Inspecting a .deb before installing it. dpkg -I and dpkg -c read a package file without touching the system, which is exactly what you want for a .deb downloaded from somewhere you are not sure about.
  • Repairing. dpkg --configure -a finishes work an interrupted install left half-done, and is often the entire fix after a machine lost power mid-upgrade or an install was killed.

Reading package states

dpkg -l puts a two-letter state code in its first column, and it is the part people skip:

  • ii: installed and configured. The normal state.
  • rc: removed, but its configuration files are still on disk. This is what apt remove leaves behind, and why a package you "removed" can still be affecting things. apt purge clears it, and remove vs purge vs autoremove is when to pick which.
  • iU or iF: unpacked or half-configured. Something went wrong; dpkg --configure -a is the usual repair.
  • un: not installed, but known about, usually because something else references it.

The first letter is what you wanted, the second is what is true. When they disagree, the package needs attention.

Installing a .deb, and why you probably shouldn't

dpkg -i file.deb installs a package file directly. If its dependencies are already present it works; if they are not, it unpacks the package, fails to configure it, and leaves it in a broken state you then have to repair.

For that reason, prefer:

sudo apt install ./file.deb

The ./ is what makes apt treat it as a file rather than a package name. apt unpacks it through dpkg exactly as dpkg -i would, but resolves and downloads the dependencies first, so the install either completes or does not start. Use dpkg -i when apt is not available or not working, which happens, and is the reason to know the command exists. Installing a .deb by hand walks the whole sequence, including what the broken state looks like and how to read the package before you run either command.

dpkg -l is for reading, not parsing

dpkg -l is for reading, not parsing: its output is a fixed-width table that truncates the description to the terminal width, and its exact columns are not a stable interface. When a script needs a fact about a package, use dpkg-query -W -f and name the fields you want, or test dpkg -s by exit status. Both are shown in the examples below.

Package operations need root. Every query on this page does not, and it is worth running them unprivileged out of habit.

17 outputs, collapsed by default

Asking what is installed

All of these read the local package database. No network, no sudo, and they keep working on a machine whose apt configuration is broken.

Check whether a package is installed

dpkg -l cowsay

The first column is the state - ii is installed and configured. The table truncates its description column to the terminal width, so treat it as something to read rather than to parse.

Show output
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  cowsay         3.03+dfsg2-8 all          configurable talking cow

Tell an installed package from a removed one

dpkg -l cowsay bash-completion | tail -3

rc means removed but not purged: the programs are gone and the configuration files are still on disk. This is the state that explains a package still affecting a system after you removed it.

Show output
+++-===============-============-============-==========================================
rc  bash-completion 1:2.16.0-7   all          programmable completion for the bash shell
ii  cowsay          3.03+dfsg2-8 all          configurable talking cow

Read everything dpkg knows about a package

dpkg -s cowsay

The full database entry - state, version, dependencies and description. Unlike dpkg -l this is a stable field-per-line format, and it needs no terminal width to be readable.

Show output
Package: cowsay
Status: install ok installed
Priority: optional
Section: games
Installed-Size: 92
Maintainer: James McDonald <james@jamesmcdonald.com>
Architecture: all
Version: 3.03+dfsg2-8
Depends: libtext-charwidth-perl, perl:any
Suggests: filters, cowsay-off
Description: configurable talking cow
 Cowsay (or cowthink) will turn text into happy ASCII cows, with
 speech (or thought) balloons. If you don't like cows, ASCII art is
 available to replace it with some other creatures (Tux, the BSD
 daemon, dragons, and a plethora of animals, from a turkey to
 an elephant in a snake).
Homepage: https://web.archive.org/web/20120527202447/http://www.nog.net/~tony/warez/cowsay.shtml

List the files a package installed

dpkg -L cowsay | head -6

Every path the package put on disk, directories included. The complete answer to "where did that command come from, and what else came with it".

Show output
/.
/usr
/usr/games
/usr/games/cowsay
/usr/share
/usr/share/cowsay

Find which package owns a file

dpkg -S /usr/games/cowsay

The reverse lookup, and the fastest way to identify an unfamiliar file. It only knows about installed packages - for a file you don't have yet, apt-file search is the equivalent.

Show output
cowsay: /usr/games/cowsay

Find which package owns a command

dpkg -S "$(command -v gzip)"

command -v resolves the name on your PATH first, so this works without knowing where the binary lives.

Show output
gzip: /usr/bin/gzip

List a package's configuration files

dpkg -s bash-completion | grep -A3 ^Conffiles

Conffiles are the files dpkg treats as yours to edit - it will not overwrite a changed one on upgrade without asking. Shown here for a package in the rc state, whose programs are gone while every one of these files is still on disk.

Show output
Conffiles:
 /etc/bash_completion a81b3f1cb197219b815942f4fc7fa94e
 /etc/bash_completion.d/000_bash_completion_compat.bash 3834d4b9bcbc9a8bdb847f1a6727ae7a
 /etc/profile.d/bash_completion.sh 3593c6b58d5ede41aedc83dff04c35f7

Inspecting a .deb before installing it

These read a package file and change nothing, which makes them the right first move for a .deb downloaded from somewhere you are not certain about.

Read a package file's metadata

dpkg -I cowsay-off.deb | head -12

Version, dependencies, maintainer and size, straight out of the file. No installation and no root required.

Show output
 new Debian package, version 2.0.
 size 8392 bytes: control archive=1024 bytes.
     871 bytes,    21 lines      control
     500 bytes,     7 lines      md5sums
 Package: cowsay-off
 Source: cowsay
 Version: 3.03+dfsg2-8
 Architecture: all
 Maintainer: James McDonald <james@jamesmcdonald.com>
 Installed-Size: 23
 Depends: cowsay (>= 3.03+dfsg2-3)
 Breaks: cowsay (<< 3.03+dfsg2-3)

List what a package file would install

dpkg -c cowsay-off.deb 2>/dev/null | head -6

Every path in the archive with its mode and owner, before anything is unpacked. The check to run when a third-party .deb might write somewhere it shouldn't.

Show output
drwxr-xr-x root/root         0 2020-05-11 06:43 ./
drwxr-xr-x root/root         0 2020-05-11 06:43 ./usr/
drwxr-xr-x root/root         0 2020-05-11 06:43 ./usr/share/
drwxr-xr-x root/root         0 2020-05-11 06:43 ./usr/share/cowsay/
drwxr-xr-x root/root         0 2020-05-11 06:43 ./usr/share/cowsay/cows/
-rw-r--r-- root/root       584 1999-08-14 07:17 ./usr/share/cowsay/cows/beavis.zen.cow

Extract a package's files without installing it

dpkg -x cowsay-off.deb /tmp/unpacked && find /tmp/unpacked -type f | sort | head -3

Unpacks the file tree into a directory of your choosing. No maintainer scripts run, nothing is registered and nothing is configured - useful for pulling one file out of a package. find reports in directory order, so sort is what makes the listing the same twice.

Show output
/tmp/unpacked/usr/share/cowsay/cows/beavis.zen.cow
/tmp/unpacked/usr/share/cowsay/cows/bong.cow
/tmp/unpacked/usr/share/cowsay/cows/mutilated.cow

Extract a package's control information

dpkg -e cowsay-off.deb /tmp/control && ls /tmp/control

Unpacks the maintainer scripts rather than the file tree. This is how to read what a package would run as root before letting it.

Show output
control
md5sums

Installing and removing directly

All of these need root, and none of them resolve dependencies. Their output is a live unpack - progress lines, sizes and your machine's architecture - so it isn't reproduced here. Prefer sudo apt install ./file.deb, which does the same unpacking with the dependencies sorted out first.

Install a package file

sudo dpkg -i cowsay-off.deb

Unpacks and configures a single .deb. Succeeds only if every dependency is already present; otherwise it leaves the package unpacked but unconfigured.

Install a .deb the way you actually should

sudo apt install ./cowsay-off.deb

The ./ makes apt treat the argument as a file rather than a package name. Same unpacking, but dependencies are resolved first, so the install either completes or never starts.

Repairing a broken install

What to run when an install was interrupted by a killed session, a machine that lost power mid-upgrade, or a full disk.

Finish configuring everything left half-done

sudo dpkg --configure -a

Configures every package that was unpacked but never configured. Often the entire fix after an interrupted upgrade, and safe to run at any time - on a healthy system it prints nothing and does nothing.

Check what state every package is in

dpkg -l | awk '$1 != "ii" && NR > 5 {print $1, $2}'

Anything whose state isn't ii - removed-but-not-purged packages and genuinely broken ones both show up here.

Show output
rc bash-completion

Reading the database in scripts

dpkg -l is a table for people. When a script needs a fact, name the field you want and nothing else can shift underneath you.

Print exactly one field

dpkg-query -W -f='${Version}\n' cowsay

-f takes a template of the fields you want. Nothing else is printed, so no header, column or description change can affect the result.

Show output
3.03+dfsg2-8

Print several fields in your own format

dpkg-query -W -f='${Package} ${Version} ${Status}\n' cowsay ca-certificates

Any field from the database, in any layout. ${Status} is the three words behind the two-letter code dpkg -l abbreviates.

Show output
ca-certificates 20250419 install ok installed
cowsay 3.03+dfsg2-8 install ok installed

Test whether a package is installed, properly

dpkg-query -W -f='${Status}' cowsay 2>/dev/null | grep -q "^install ok installed" && echo yes || echo no

More precise than dpkg -s, which succeeds for a package in the rc state too. This asks specifically whether the package is currently installed.

Show output
yes

List every package marked as held

dpkg --get-selections | grep hold || echo "nothing held"

dpkg records holds alongside apt's, so this and apt-mark showhold answer the same question from either side.

Show output
nothing held