Explore an Image's Filesystem
Browse what an image contains, layer by layer, and open any file, from the web UI.
Prerequisites
- Angos running with the web UI enabled
- Readers with the
get-blobaction on the namespace, which is what the layer endpoints check
How It Works
- A layer is a tar stream, usually gzipped. An
indexjob walks it once and keeps, by the layer digest, a listing of its entries with their offsets, each file's SHA-256, SHA-512 and media type and, for a gzipped layer, an inflater checkpoint every 4 MiB of output. - The manifest page fetches the listing of each layer and merges them the way a runtime does: an entry replaces the lower layers' one, a whiteout removes a path, an opaque marker empties a directory.
- Opening a file reads only the chunk of the listing holding its path and the chunk of checkpoints before its offset, then decodes the layer from the nearest checkpoint, so a file deep in a large layer costs about a megabyte of listing and a few megabytes of decoding, not the whole layer.
A listing an older version wrote may lack the files' digests, media types or secrets; it is served as it is while an index job walks its layer again, and the tab says so and swaps in the new listing when the job is done. The listing is derived from the blob and shared like it: two images with the same layer share one listing, and it goes when the blob is reclaimed, or when angos reconcile index runs while no image an index policy applies to uses the layer. zstd-compressed layers are not indexed.
Step 1: Open an Image
Open any image manifest in the web UI. Its page ends with a Filesystem panel. An image nobody indexed yet shows "Indexing" while the registry, or a worker, walks its layers; a few seconds for a small image, longer for a large one.
Step 2: Browse
Folders open on click, as a tree in the Tree view or as tiles walked with the folder path in the Icons view. The layer column names the layer that last set each entry, L1 being the lowest. Check one or more layers in the layers menu to see only what those layers added, changed or removed, the removed paths struck through, or type in the filter to narrow the tree to matching paths.
The keyboard walks every view: the arrow keys move through the list, left and right also fold and unfold a folder of the tree, Enter opens what is under the cursor, and Backspace climbs out of a folder of the Icons view.

Step 3: Open a File
Click a file to open it beside the list, which stays in view while the list scrolls: its layer, media type, size, mode, owner, modification date and digests over its text with numbered lines, syntax colored when its media type names a known language. A markdown file shows rendered, an image as the picture, an ELF binary its type, architecture, linking, the libraries it needs, its RELRO and whether its stack is executable, entry point and build ID, and a PEM file lists its certificates, subject, issuer, names and expiry, the expired ones flagged; Source shows any of them as text. Diff compares the file with its version in the layer below, or above for the lowest, when another layer holds one. A file larger than 512 KiB shows its first 512 KiB; Download fetches it whole. When several layers hold the path, their chips switch between the versions. A hard link opens its target. A symlink shows where it points and, clicked, is followed there, through any links on the way; one that leads out of the image says so. Esc or the close button puts the file away; on a phone it takes the whole screen until then.
The open file is in the URL, #filesystem/usr/bin/entrypoint, with @L2 after the path for a version a later layer overwrote, so a link opens it and the browser's back button returns to the file before.


Step 4: Check Secrets, Permissions and Wasted Space
The Secrets view, offered when there are any, lists the files that hold a private key, a GitHub, GitLab, Slack or Stripe token, an AWS access key ID, or AWS, Docker, npm, Git, netrc or kubeconfig credentials, anywhere in a text file of any layer, with the lines they are on: one a later layer removed is gone from the running image but still ships in its own layer, for anyone who pulls it. Opening one shows its text with those lines marked, scrolled to the first.

The Permissions view, offered when there are any, lists what the image runs with raised privileges or lets anyone change: setuid and setgid files, files granted Linux capabilities such as cap_net_bind_service, and world-writable files and folders, a sticky one such as /tmp aside. A folder opens in the tree.

The Waste view measures, against the image's uncompressed size, the bytes of files a later layer overwrote or removed, which still weigh in their own layer, and the bytes spent on copies of identical files. Its two lists, the largest first, tell a file replaced from one rewritten unchanged, such as by a chmod or chown in a later step, or removed. An overwritten or removed file opens as its own layer still holds it.

Step 5: Index on Push
Indexing on first open is fine for a registry browsed now and then. A repository whose images are opened as soon as they land indexes them as they land:
[repository."apps".index]
default = "index"
To index every repository this way, put the policy under [global] instead:
[global.index]
default = "index"
An index table is a policy shaped like an access policy, the same as a scan table: default is index or skip, skip when absent, and rules over the retention variables give a matching image the opposite, judged as the image lands under its pushed tags and again by reconcile index. rules = ["image.tag == 'latest' || top_pulled(20)"] with no default indexes the tags people open and leaves the rest to their first open.
Each image manifest pushed there, or stored by a cache miss in a pull-through repository, enqueues one index job per layer. angos worker drains the queue, sized by max_concurrent_index_jobs (default 4, a job inflating one layer on one core), and the server drains it in-process without a durable queue. The jobs list under the index queue of the Jobs page.
Step 6: Index What Was Already There
The policy covers images from then on. To have the listings of the images already in the repository ready before anyone opens them:
angos -c config.toml reconcile index
It enqueues one job per tar layer without a listing, a shared layer once, and reclaims the listings of the layers no image an index policy applies to uses; --dry-run lists both, and --force walks every layer again. The server or a worker drains the jobs as usual.
To stop indexing a repository, drop its index table, or give it default = "skip" under a global policy, and run the same command: the listings its images accumulated go, while any image still indexes itself the first time someone opens its filesystem.
Reference
- API Endpoints Reference - The layer endpoints
- Configuration Reference - The
indextable - Web UI Reference - The Filesystem panel