This apt cheat sheet sorts the commands you’ll actually type on Debian and Ubuntu by task. It covers install, remove, update, search, hold and fix-broken, then settles the apt vs apt-get question. Tables come first for copying, and the sections below show what each command prints, including the dependency errors that send people searching.
Commands new in apt 3.1, such as history-undo and apt why, are flagged, and a release table at the end shows which apt your distro ships. Every command and output below was built and run on Ubuntu 26.04.1 LTS with apt 3.2.1 in November 2026, so the output matches the newer apt 3 layout.
APT Cheat Sheet (Quick Reference)
Commands that change the system need sudo and read-only queries do not. On apt 3 the query commands also open in a pager when you run them in a terminal, so press q to get your prompt back.
Update and upgrade
| Command | What it does |
|---|---|
sudo apt update | Refresh the package index from every configured source |
apt list --upgradable | Show packages with a newer candidate version |
sudo apt upgrade | Upgrade installed packages, never removing any |
sudo apt full-upgrade | Upgrade and allow removals to resolve conflicts |
sudo apt install --only-upgrade nginx | Upgrade one package if it’s installed, never install it fresh |
sudo apt -o APT::Get::Always-Include-Phased-Updates=true upgrade | Take Ubuntu phased updates now instead of waiting |
apt full-upgrade -s | Simulate the upgrade and change nothing |
Install packages
| Command | What it does |
|---|---|
sudo apt install nginx | Install a package plus its dependencies and recommends |
sudo apt install -y jq | Install without the confirmation prompt |
sudo apt install nginx=1.28.3-2ubuntu1 nginx-common=1.28.3-2ubuntu1 | Install an exact version, pinning strict siblings on the same line |
sudo apt install ./package.deb | Install a local .deb and pull its dependencies from the repos |
sudo apt install --no-install-recommends fortune-mod | Skip Recommends, install hard dependencies only |
sudo apt reinstall tree | Reinstall the current version over itself |
apt download figlet | Download the .deb into the current directory, no install |
Remove and clean up
| Command | What it does |
|---|---|
sudo apt remove nginx | Remove the package, keep its config files in /etc |
sudo apt purge nginx | Remove the package and its config files |
sudo apt autoremove | Remove dependencies nothing needs any more |
sudo apt autoremove --purge | Same, and delete their config files too |
sudo apt purge '~c' | Purge every removed package that left config behind (rc state) |
sudo apt clean | Empty the downloaded .deb cache in /var/cache/apt/archives |
sudo apt autoclean | Delete only cached .debs that can no longer be downloaded |
Search and inspect
| Command | What it does |
|---|---|
apt search --names-only '^nginx' | Search package names only, regex allowed |
apt show nginx | Version, size, dependencies and description |
apt show -a nginx | Show every available version, not just the candidate |
apt policy nginx | Installed and candidate version, with the repo and priority of each |
apt-cache madison nginx | One line per available version and source |
apt list --installed | Every installed package with version and state |
apt depends nginx | What a package depends on |
apt rdepends --installed libonig5 | Which installed packages depend on this one |
Hold and mark packages
| Command | What it does |
|---|---|
sudo apt-mark hold nginx nginx-common | Freeze packages at their current version |
apt-mark showhold | List held packages |
sudo apt-mark unhold nginx nginx-common | Release the hold |
apt-mark showmanual | Packages you installed by name |
apt-mark showauto | Packages pulled in as dependencies (autoremove candidates) |
sudo apt-mark manual figlet | Mark a package manual so autoremove never takes it |
Fix broken installs
| Command | What it does |
|---|---|
sudo apt --fix-broken install | Install missing dependencies for half-installed packages |
sudo dpkg --configure -a | Finish configuring packages left unpacked by an interrupted run |
sudo dpkg --audit | List packages that are unpacked but not configured |
sudo apt-get check | Check the dependency tree for unmet dependencies |
sudo apt-get -o DPkg::Lock::Timeout=60 install tree | Wait up to 60 s for the dpkg lock instead of failing |
History and why (apt 3.1+)
| Command | What it does |
|---|---|
sudo apt history-list | Numbered list of past apt transactions |
sudo apt history-info 7 | Every package one transaction changed |
sudo apt history-undo 7 | Reverse one transaction |
apt why libonig5 | Trace which package pulled a dependency in |
apt why-not apache2 | Explain a solver decision (never-requested packages show “undecided”) |
zgrep -h Commandline /var/log/apt/history.log* | Past apt command lines on apt 2.x releases |
dpkg queries
| Command | What it does |
|---|---|
dpkg -l nginx | Install state of a package (ii, rc, iU, un) |
dpkg -L curl | Files a package installed |
dpkg -S /usr/bin/curl | Which package owns a file |
sudo dpkg -i package.deb | Install a local .deb with no dependency resolution |
The rest of the page walks through the same commands in task order, with the output each one gave on the lab box and the fix for the errors that came up along the way.
Update the Index and Upgrade Packages
apt update only downloads package lists and does not change a single installed file. Run it before any install or upgrade, because apt resolves versions against whatever index it last fetched. A stale index makes apt ask the mirror for .deb files that have already rotated out, so the install fails with a 404 halfway through.
sudo apt update
The last line tells you whether anything is waiting. Here four indexes were already current and two packages had updates:
Hit:1 http://archive.ubuntu.com/ubuntu resolute InRelease
Hit:2 http://archive.ubuntu.com/ubuntu resolute-updates InRelease
Hit:3 http://archive.ubuntu.com/ubuntu resolute-backports InRelease
Hit:4 http://security.ubuntu.com/ubuntu resolute-security InRelease
2 packages can be upgraded. Run 'apt list --upgradable' to see them.
List them before you upgrade so you know what’s about to move. A kernel, glibc or database server in this list deserves a maintenance window, because those need a reboot or a service restart to take effect. A couple of Python helpers don’t, so you can patch them during the day. Each line shows the repo the new version comes from, the new version and the one you’re on.
apt list --upgradable
Both packages here come from resolute-updates, the regular bug-fix pocket.
python3-software-properties/resolute-updates 0.120.1 all [upgradable from: 0.120]
software-properties-common/resolute-updates 0.120.1 all [upgradable from: 0.120]
Both steps together, as they ran on the lab VM:

Now upgrade. apt upgrade never removes a package, which makes it the safe default for routine patching. If an update would need a removal, apt keeps that package back and tells you instead of guessing.
sudo apt upgrade -y
On Ubuntu this often upgrades nothing, and that is not a bug. The two packages from the list above were held back by phasing.
The following package was automatically installed and is no longer required:
pollinate
Use 'sudo apt autoremove' to remove it.
Not upgrading yet due to phasing:
python3-software-properties software-properties-common
Summary:
Upgrading: 0, Installing: 0, Removing: 0, Not Upgrading: 2
Phased updates go out to a growing percentage of Ubuntu machines over a few days, so a bad build hits a small slice first and gets pulled before it reaches everyone. Your box will get them on its own, and Ubuntu’s phased updates page explains how the rollout percentage is picked. Override it only when you have a reason, such as a security fix you’re tracking or two servers that must run the same build. The override applies to one run.
sudo apt -o APT::Get::Always-Include-Phased-Updates=true upgrade -y
The same two packages now move.
Upgrading:
python3-software-properties software-properties-common
Summary:
Upgrading: 2, Installing: 0, Removing: 0, Not Upgrading: 0
Download size: 45.3 kB
Space needed: 0 B / 16.9 GB available
Use sudo apt full-upgrade when an upgrade needs to remove or replace packages, which is what new kernel series and library transitions do. Run it with -s first. The simulation prints the same plan and touches nothing, so you see any removal before it happens. If apt itself is behind on a security fix, patch it before anything else, and the APT CVE-2026-109518 guide covers checking the installed apt build.
Install Packages, Versions and Local .deb Files
A plain sudo apt install nginx installs the candidate version, its dependencies and its Recommends. Before pinning a version, ask apt which ones exist, because the version string has to match exactly, Debian revision and Ubuntu suffix included. The upstream release number alone (1.28.3) is not a valid version string here.
apt policy nginx
The release pocket carries the original build and resolute-updates carries the patched one, which is the candidate.
nginx:
Installed: (none)
Candidate: 1.28.3-2ubuntu1.12
Version table:
1.28.3-2ubuntu1.12 500
500 http://archive.ubuntu.com/ubuntu resolute-updates/main amd64 Packages
500 http://security.ubuntu.com/ubuntu resolute-security/main amd64 Packages
1.28.3-2ubuntu1 500
500 http://archive.ubuntu.com/ubuntu resolute/main amd64 Packages
Asking for the older build with nginx=1.28.3-2ubuntu1 on its own fails. nginx depends on nginx-common of the exact same version, and apt won’t pick an older sibling you didn’t name, so the solver reports two conflicting assignments and stops. Check the Depends: line of apt show for any (= version) entry before you pin, since that is the list of siblings you’ll have to name too.

Pin every package that carries an (= version) dependency on the same line.
sudo apt install -y nginx=1.28.3-2ubuntu1 nginx-common=1.28.3-2ubuntu1
apt now resolves both and shows the plan before it downloads anything.
Installing:
nginx nginx-common
Suggested packages:
fcgiwrap nginx-doc ssl-cert
Summary:
Upgrading: 0, Installing: 2, Removing: 0, Not Upgrading: 2
Download size: 652 kB
Space needed: 1853 kB / 16.5 GB available
Pinning a version doesn’t keep it there. The next apt upgrade moves it straight back to the candidate unless you hold it, which is the next section. Naming nginx-common also marked it manually installed, so a later autoremove won’t clean it up for you.
For local .deb files, prefer sudo apt install ./package.deb over dpkg -i. A path with a slash in it tells apt the argument is a file, and apt then fetches the dependencies from your repos. With fortune-mod, the apt route installed three packages while the dpkg route broke, which the fix-broken section reproduces. Add --no-install-recommends when you want a lean server, because Recommends often drag in docs and helpers you never use. A simulation of the same fortune-mod install dropped from 3 packages to 2 with the flag.
Keep a Package Back with apt-mark hold
A hold stops apt upgrade and full-upgrade from touching a package. It’s the right tool for a pinned database, a Kubernetes node’s kubelet, or the nginx build you just tested, because the next routine upgrade would otherwise undo your pin without asking. The workflow is short.
- Hold the package and every sibling with a strict version dependency.
- Confirm the hold with
apt-mark showhold. - Upgrade as normal and check that the held packages land under “Not upgrading”.
Hold both nginx packages. Holding nginx alone means nginx-common gets kept back too, because upgrading it would break nginx’s strict version dependency, and an explicit apt install nginx-common would then fail with the same error you hit when pinning.
sudo apt-mark hold nginx nginx-common
apt-mark showhold
An upgrade afterwards keeps them back and lists them in their own “Not upgrading” block, separate from the phased packages. The summary count of 4 is the two held plus the two phased ones.

A hold stays in force during a release upgrade too, so list holds before you start one. The Debian 12 to 13 upgrade guide checks for them in its pre-flight. When you’re ready to move, release the hold:
sudo apt-mark unhold nginx nginx-common
apt confirms each one:
Canceled hold on nginx.
Canceled hold on nginx-common.
To bring just those two up to date without running a full system upgrade, use sudo apt install --only-upgrade nginx nginx-common. It upgrades installed packages only and refuses to install a package you don’t already have, which makes it safe in scripts that loop over a host list. The single package upgrade guide covers the edge cases.
Remove vs Purge and Clean the Cache
remove deletes the program and keeps its configuration, so a reinstall picks up your old settings. purge deletes both, which is what you want when the config itself is the problem. On the lab box, apt remove nginx left nginx-common installed and /etc/nginx in place, because nginx-common had been named on the install line and was marked manual. Purging nginx-common removed the directory.

Purge prints an asterisk after each package name in its plan, which is the quickest way to tell the two apart before you confirm.
sudo apt purge -y nginx-common
Look for the * and the “Purging configuration files” line.
REMOVING:
nginx-common*
Summary:
Upgrading: 0, Installing: 0, Removing: 1, Not Upgrading: 2
Freed space: 232 kB
Removing nginx-common (1.28.3-2ubuntu1.12) ...
Purging configuration files for nginx-common (1.28.3-2ubuntu1.12) ...
Plain autoremove has the same gap as remove. On the lab VM it removed the unused pollinate package but left its config behind, so dpkg -l still listed it in the rc state, meaning removed with config remaining. Those entries pile up for years on long-lived servers and make dpkg -l output harder to read. The ~c pattern matches every package in that state, so one purge clears them all:
sudo apt purge '~c'
The download cache is the other thing that grows, though not from apt itself: apt deletes each .deb after a successful install unless APT::Keep-Downloaded-Packages is set to true. apt-get and unattended-upgrades keep theirs. After the cloud image’s apt-get dist-upgrade, /var/cache/apt/archives held 232 .deb files and 383 MB, and sudo apt clean took it down to 28 KB. That matters on small VMs and container images, where a forgotten cache can be the difference between a full root disk and a healthy one. For a full cleanup after removing software, run these in order:
- Purge the package you no longer want with
sudo apt purge nginx. - Drop its orphaned dependencies and their config with
sudo apt autoremove --purge. - Clear any older
rcleftovers withsudo apt purge '~c'. - Empty the download cache with
sudo apt clean, or usesudo apt autocleanto keep .debs that are still current.
Step 4 is safe to run any time, since apt simply downloads a package again the next time it needs one.
Fix Broken Packages and Unmet Dependencies
The classic way to break apt is installing a downloaded .deb with dpkg -i. dpkg unpacks the file but has no idea where to get dependencies, so it leaves the package unconfigured and every later apt command refuses to run. Vendor downloads (browsers, VPN clients, chat apps) are where most people hit this. These two commands reproduce it with a small package from the Ubuntu archive. They break apt on purpose, so run them on a test VM, not a server you care about.
cd /tmp && apt download fortune-mod
sudo dpkg -i /tmp/fortune-mod_*.deb
dpkg stops on the missing librecode3. Worse, the next unrelated install fails too, because apt refuses to plan anything while one package sits half-installed, and it says so with “Unmet dependencies”.

Run the fix apt suggests, with no package names. It installs whatever the half-installed package needs and finishes configuring it. Adding a package name here makes apt try to solve both jobs at once, which is rarely what you want while the system is broken.
sudo apt --fix-broken install -y
“Correcting dependencies… Done” and the “Setting up fortune-mod” line mean the system is consistent again.

There’s a catch the summary hides. --fix-broken installed only the hard dependency, librecode3, and skipped the Recommends. Running fortune afterwards printed “No fortunes found” because the data package, fortunes-min, never came along. When apt install ./fortune-mod...deb was used instead, apt pulled all three packages and the command worked first time. That’s the practical reason to stop using dpkg -i for anything with dependencies.
A different message, “dpkg was interrupted”, means a run died between unpacking and configuring, usually because an SSH session dropped or the OOM killer hit dpkg. Nothing is missing from the repos in that case, so --fix-broken is the wrong first move. Ask dpkg what it left behind instead. On the lab box a package unpacked with dpkg --unpack showed up in the iU state.
sudo dpkg --audit
The audit names each package that still needs configuring:
The following packages have been unpacked but not yet configured.
They must be configured using dpkg --configure or the configure
menu option in dselect for them to work:
figlet Make large character ASCII banners out of ordinary text
Finish the job for every stuck package at once.
sudo dpkg --configure -a
The package moves from iU to ii, fully installed.
Setting up figlet (2.2.5-3.1) ...
update-alternatives: using /usr/bin/figlet-utf8 to provide /usr/bin/figlet (figlet) in auto mode
If you hit a broken state, work through the fixes in this order and stop at the first one that clears it:
- Run
sudo dpkg --configure -ato finish anything half-configured. - Run
sudo apt --fix-broken installto fetch missing dependencies. - Run
sudo apt-get checkand confirm it exits without listing unmet dependencies.
Never delete /var/lib/dpkg/lock-frontend to get past “Could not get lock”. apt’s own error warns against it, and the lock means another process (often unattended-upgrades right after boot) is mid-transaction. Wait for it, or see the lock behaviour in the apt vs apt-get section.
Use apt history to Undo an Install
apt 3.1 added a transaction history you can act on, the closest thing Debian systems have had to dnf history undo. It ships with Ubuntu 26.04, while Ubuntu 24.04 and Debian 13 do not have it yet. Before this, undoing an install meant reading /var/log/apt/history.log and retyping the package list by hand, which is how people end up removing the wrong library. List the transactions first.
sudo apt history-list
Transaction 7 was the apt install ./fortune-mod...deb run that pulled three packages. history-undo 7 reverses exactly that transaction and nothing else:

The undo removes packages but does not purge them, so config files stay behind. Always read a transaction with history-info before reversing it, because the ID alone tells you very little. On the lab VM, sudo apt history-info 2 turned out to be the 216-package image upgrade with a new kernel in it, which is not something to undo casually. apt --help on apt 3.2 also lists history-redo and history-rollback.
apt why answers the other common question: what pulled this library in? It walks the dependency chain back to something you installed:
apt why libonig5
The trace shows jq as the reason, so removing jq makes libonig5 an autoremove candidate:
libonig5:amd64 is selected for install because:
1. jq:amd64 is selected for install
2. jq:amd64 Depends libonig5 (>= 6.8.1)
A trace that stops at (root) with no dependency chain under it usually means nothing installed needs the package. On the lab box that was pollinate, the same package apt upgrade kept flagging for autoremove, so cross-check with apt rdepends --installed before removing anything. On apt 2.x, apt rdepends --installed libonig5 and /var/log/apt/history.log give you the same answers with more reading.
Choose Between apt and apt-get
Use apt at the terminal and apt-get (plus apt-cache) in scripts, Dockerfiles and Ansible shell tasks. Both drive the same libapt and dpkg, so they install identical packages. The difference is the interface, and the apt(8) manual says plainly that apt’s output can change between versions while apt-get’s stays stable. apt even warns you when you pipe it:
apt list --installed | head -3
The warning goes to stderr, ahead of the package lines:
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Listing...
3cpio/resolute,now 0.14.0-1ubuntu1 amd64 [installed,automatic]
adduser/resolute,now 3.153ubuntu1 all [installed,automatic]
The lock is where the two behave most differently. With another process holding the dpkg lock, apt-get failed instantly with exit code 100, while apt sat on “Waiting for cache lock” and kept counting:

Failing fast is usually what a script wants, but a provisioning run that starts while unattended-upgrades is busy then dies on boot. Add -o DPkg::Lock::Timeout=60 so apt-get waits like apt does. In scripts, also set the noninteractive frontend so no debconf prompt can block the run:
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq cowsay
The rest of the differences, side by side:
| Behaviour | apt | apt-get / apt-cache |
|---|---|---|
| Meant for | People at a terminal | Scripts, CI, config management |
| Output format | Can change between releases (apt 3 added the Summary block) | Stable, safe to parse |
| Held dpkg lock | Waits with a counter (indefinitely in a terminal, 120 s when not on a TTY) | Fails with exit 100 unless DPkg::Lock::Timeout is set |
| Pager | list, show, search and policy open in a pager on apt 3 | Never |
| Progress bar and colour | Yes | No |
| Search and show | apt search, apt show | apt-cache search, apt-cache show |
| Upgrade with removals | apt full-upgrade | apt-get dist-upgrade |
| History, why, satisfy | Yes on apt 3.1+ | No |
apt-get is not deprecated and isn’t going away, because it is the scripting interface. If the pager gets in your way in a terminal, set APT_PAGER=cat in your shell profile. The equivalent cheat sheet for Fedora is the DNF5 commands cheat sheet, and the add-apt-repository guide covers adding PPAs and third-party repos.
Check Which apt Your Release Ships
Most commands on this page work on any supported release. The newer ones don’t, so check apt --version first and match it to this table, built from the Ubuntu and Debian package archives and the apt changelog:
| Release | apt version | Summary output and pager | apt why / why-not | history-list / undo |
|---|---|---|---|---|
| Ubuntu 22.04 LTS | 2.4.15 | No | No | No |
| Ubuntu 24.04 LTS | 2.8.4 | No | No | No |
| Debian 12 | 2.6.1 | No | No | No |
| Debian 13 | 3.0.3 | Yes | No | No |
| Ubuntu 26.04 LTS | 3.2.1 | Yes | Yes | Yes |
Only the Ubuntu 26.04 row was run hands-on for this page. The other versions come from packages.ubuntu.com and tracker.debian.org in November 2026, and the feature columns follow the apt changelog: the pager arrived in 2.9.18, why in 3.1.0, history in 3.1.6 and undo in 3.1.7. Fresh from install? The Ubuntu 26.04 server post-install checklist puts these commands to work.
More printable references for Linux and DevOps tools live on the cheat sheets page.