Apps — images#
The Images tab of Services → Apps lists the container images on the router's disk. Images are pulled during an install and kept until something removes them, so this tab is where disk goes back after an app is uninstalled or after an update leaves a superseded layer set behind.
The list comes from show container image on the router, parsed as an aligned table and
returned inside GET /api/apps/installed as images. It is the router's own view, not
the agent host's — an off-router agent describes the router it manages, not the machine
it runs on.
The list#
One row per image, with:
| Column | From |
|---|---|
| Repository | REPOSITORY |
| Tag | TAG |
| Size | SIZE |
| — | a Remove button, operator role only |
The same table supplies the Image ID, Image built and Image size rows on an app's overview, matched by repository.
Removing an image#
Remove calls DELETE /api/apps/image?image=<repository>:<tag>. Three things happen
in that handler before anything is deleted —
agent/apps.go, handleAppImageDelete:
- The reference is validated against the same OCI grammar the pull path uses. The router hands the name to podman through a shell, so an unvalidated reference is a command line on the firewall.
- Every installed app is checked, ignoring tags. If any declared container uses this
repository the request is refused with
409and names the app: "adguard still uses this image — remove the container first." - The result is audited either way, as
app-image-deletewith the image reference in the path field and the router's own error when it failed.
The refusal in step 2 is a comparison of base names, so :latest and :1.2.3 of the
same repository are treated as the same image for the purpose of "is anything using
this". That is the safe direction: it can refuse a removal you could have made, and it
cannot break a running container.
From the CLI, the same two operations:
show container image
delete container image docker.io/adguard/adguardhome:latestWhy an image can outlive its app#
Uninstalling an app stages delete container name <x>, which removes the declaration.
The image stays. That is deliberate — a reinstall then needs no pull — and it is why
this tab exists. The same applies to volumes: sources under /config are never removed
by an uninstall, so nobody loses a password vault to a mis-click.
An update check on an installed app re-pulls its image. Podman only downloads layers that moved, but a genuinely new image leaves the old one on disk until it is untagged or removed. If disk is tight after several updates, this is the tab to look at.
What this tab does not do#
- No pull from here. Images are pulled by the install flow or by an update check on an installed app. There is no "add image by reference" field.
- No prune. There is no bulk clean-up, no dangling-layer sweep and no size budget. Removal is one image at a time, by hand.
- No digest. The table shows repository and tag. The catalogue names tags, not digests, so what a tag pointed at when you pulled it is not recorded anywhere in the product.
See also#
- Apps — where images get pulled in the first place.
- One app — the update check that compares image IDs.
- System — overview — the disk meter these images consume.
Checked against agent/apps.go,
agent/vyos.go,
ui/src/pages/Apps.tsx,
ui/src/pages/AppDetail.tsx,
agent/main.go, docs/apps.md.