A provisioning run on Ubuntu logs the line "WARNING: apt does not have a stable CLI interface. Use with caution in scripts." after `apt install -y nginx`. What is that warning telling you, and what should the script use instead?
answer
- end-user front end, not an API
- warning fires when stdout is not a tty
- apt-get and apt-cache are the stable pair
- defaults differ, not just formatting
- dpkg-query for machine-readable facts
basics
~20 sThe apt front end is an end-user tool whose output format and defaults may change between releases, so scripts must not depend on it. Use apt-get and apt-cache instead: they have a stable, documented interface, and apt prints that warning whenever its output is not a terminal.
solid answer
~40 s`apt` is the human-facing front end that combines the most common `apt-get` and `apt-cache` operations and adds a progress bar, colour and a friendlier listing format. Its manual page says outright that it is designed as an end-user tool and may change behaviour between versions, so `apt-get` and `apt-cache` remain the interfaces with backward-compatibility guarantees. The warning fires whenever apt notices its stdout is not a terminal — that is, whenever something is capturing or piping it, which in practice means a script. The fix is to write `apt-get install -y nginx` and `apt-cache policy nginx` in automation, keeping `apt` for interactive work. It is not a cosmetic difference either: defaults genuinely differ, most visibly that `apt upgrade` installs new packages where `apt-get upgrade` refuses to.
code
bash · 8 linesset -euo pipefail
# Stable interface for automation
apt-get update
apt-get install -y --no-install-recommends nginx
# Machine-readable fact, stable format string
dpkg-query -W -f='${Package} ${Version} ${Status}\n' nginxgo deeper
Know that apt is the friendly interactive command and apt-get is the one that belongs in scripts, and that the warning is advice about the interface rather than a sign the install broke.
Explain that the warning fires because stdout is not a terminal, name apt-get and apt-cache as the stable pair, and give at least one real behavioural difference such as new-package handling during upgrade.
Demonstrate the automation habits: apt-get with -y, --no-install-recommends, a decided conffile policy, simulation before committing, and dpkg-query rather than scraped output for facts other tooling depends on.
Set the convention across the estate — which commands are permitted in configuration management, how upgrade semantics are pinned so a refactor cannot silently change patch behaviour, and where interactive tools like aptitude are allowed at all.
## What `apt` actually is `apt` arrived as a front end over the same libapt machinery that `apt-get` and `apt-cache` use. It is not a new package manager; it is a curated subset of the two older commands with better ergonomics: `apt install`, `apt remove`, `apt update`, `apt upgrade`, `apt full-upgrade`, plus `apt search`, `apt show` and `apt list` lifted from `apt-cache`, and extras such as `apt edit-sources`. With the ergonomics come things a script must not rely on: a progress bar, coloured and column-aligned output whose width depends on the terminal, and listing formats that have been adjusted more than once between releases. Its own man page states that the command line is intended for interactive use and may change between versions, and recommends the older commands where backward compatibility matters. ## Why the warning appears where it does apt prints the warning when its standard output is not a terminal. That is a good proxy for "a program is reading this", which is why you see it in CI logs, configuration-management output and anything under `bash -c "… | tee"`, and never when you type the command yourself. It is not an error, the install still proceeds, and the exit status is unaffected — it is a maintainer telling you that your parser is built on sand. ## The scripting substitutes ```bash apt-get update apt-get install -y --no-install-recommends nginx apt-cache policy nginx dpkg-query -W -f='${Version}\n' nginx ``` * `apt-get` for state-changing operations, with `-y` to assume yes and `-q` to quieten the progress output. * `apt-cache policy` and `apt-cache madison` for querying what is available and from where. * `dpkg-query -W -f='…'` when you need a machine-readable fact about an installed package. Its format string is a stable contract, unlike the human-oriented output of `apt list --installed`. * `apt-get -s` to simulate a transaction and inspect the plan before committing to it. Two further flags belong in unattended runs. `--no-install-recommends` stops apt from pulling in the Recommends of each package, which on a server routinely means dragging in a documentation or desktop-adjacent stack nobody asked for. And `-o Dpkg::Options::="--force-confold"` decides in advance what happens when a package ships a changed configuration file that you have also edited locally — without it, an unattended run can stall on that prompt. ## Behaviour really does differ The warning is about interface stability, but the defaults differ too, and that is the trap that bites teams who assume the commands are aliases: * `apt upgrade` installs new packages when an upgrade needs them; `apt-get upgrade` does not, and holds them back instead. A patch job silently changes meaning if someone "modernises" the command. * `apt` shows different information at the end of a transaction and manages the terminal differently, so log output between the two is not comparable. If you deliberately want the apt behaviour in a script, express it on the stable command — `apt-get upgrade --with-new-pkgs` — rather than switching front ends. ## Where aptitude fits `aptitude` is a third front end, with an interactive text UI and a different dependency resolver that can offer several candidate solutions to a conflict and let you step through them. It is genuinely useful on Debian for untangling a messy dependency situation by hand, and it tracks automatically-installed state in its own way. It is not installed by default on modern servers and its resolver's choices differ from apt's, so it is a debugging tool to reach for consciously — not the command your fleet automation should standardise on. ## Exit status Apt-family commands return 0 on success and a non-zero status (100 for most errors) on failure, so a script with `set -e` will stop as expected. What you must not do is decide success by grepping the human-readable output — that is precisely the surface the warning says can change.
- Beyond output formatting, name a concrete behavioural difference between `apt upgrade` and `apt-get upgrade`.`apt upgrade` will install new packages when an upgrade requires them, while `apt-get upgrade` refuses and reports those packages as kept back. That is why kernel meta-package upgrades progress under one and stall under the other. If a script needs the newer behaviour, spell it out as `apt-get upgrade --with-new-pkgs` rather than swapping front ends.
- Your script needs the installed version of a package as a bare string. What do you call?`dpkg-query -W -f='${Version}\n' nginx`. The format string is a stable, documented contract and the output has no decoration to strip. Parsing `apt list --installed` or `apt show` instead means depending on human-oriented formatting that has changed between releases — exactly what the warning is about.
- When is `aptitude` worth reaching for?Interactively, on Debian, when a dependency conflict resists apt: its resolver proposes several alternative solutions and lets you accept or reject each one, which is far easier than reading a single refusal. It is not installed by default and its resolver differs from apt's, so treat it as a hands-on debugging tool rather than the command your automation standardises on.
saying these in an interview costs you the question
- Treats apt and apt-get as pure aliases with identical defaults
- Suppresses the warning instead of switching to apt-get
- Parses apt's human-readable output to detect success
- Thinks the warning means the install may have failed
- Standardises fleet automation on aptitude because it looks friendlier