Build once, promote by digest
Promoting an app from staging to production reuses the exact image staging runs. Nothing is rebuilt, so what you tested is what ships. An image pulled from a registry is pinned to its digest at deploy time, and a promotion carries that digest to the target app. The image’s lineage lists the build, each promotion and each rollback.Where to find it
- Dashboard
- CLI
- API
Images (Deploy section of the sidebar). The table lists each image with size, the apps that use it and its status. Pick a row to see:
- Lineage: every deploy that used the image, with app, source (git push, manual, promoted, rollback), branch, commit and time.
- Layers: size of each layer and its share of the image, largest first to find what to slim down.
- Verify signature: checks the image against your public key.
What cleanup never removes
An image is kept when any of these is true:- it is the live image of an app
- it is a pinned rollback target
- it is one of the newest successful deploys of any app (default 3 per app)
- a container uses it
- it is younger than the minimum age (default 24 hours)
- this platform did not build or deploy it (for example a database image you pulled by hand)
APP_IMAGE_KEEP_PER_APP and APP_IMAGE_MIN_AGE_HOURS.
Signature checks
Images signed with a cosign key pair (ECDSA P-256) can be verified against your public key. SetAPP_IMAGE_VERIFY_PUBKEY_FILE to the PEM file. Verification then works from the image page and the CLI.
Set APP_IMAGE_VERIFY_MODE=enforce to also require a valid signature on every promotion. An image that is unsigned, signed by another key or not pinned to a registry digest is refused. Images built on the server have no registry digest and cannot be promoted while enforcement is on.
The Validate button in the Images page signature card (and levelrail-cli images policy [--ref repo@sha256:...]) checks the policy itself. It reports a key file that could not be read, enforcement requested with no usable key (nothing is enforced then), and a key loaded while enforcement is off. With a reference it also verifies that one image against your key.
Limits
- Per node inventory works for the control plane’s own node today. Remote agents answer once they report images.
- Signature checks read the
sha256-<digest>.sigtag cosign pushes next to the image. Keyless (Fulcio) signatures are not supported.
Docker engine health
The top of the Images page shows whether the Docker Engine API answers, the engine version, the node platform and how much storage can be reclaimed. When most of Docker’s storage is unused, a Free space button opens the image cleanup. Re-check probes again. From the CLI:Dockerfile check
Paste a Dockerfile on the Images page, or runlevelrail-cli images lint ./Dockerfile, to get hints for a root final user, a floating base tag (no tag or :latest), no HEALTHCHECK, build tools shipped in a single stage, and a missing .dockerignore. Apply safe fixes (or --fix) adds a non-root USER 65532:65532 before the final CMD and writes a starter .dockerignore. Check that your app can run as that user before shipping.
Update and architecture status
GET /api/v1/apps/{name}/image-status (or levelrail-cli images status <app>) says whether the app’s tag now points at a newer digest and whether the image architecture matches the node. The app’s auto-update card shows an Update now button when either applies. Private registry credentials are stored encrypted and used for the lookup, never returned.