Container image standard
Rules
| Identifier | Rule |
|---|---|
base | The final stage starts from the CommonLayer base image, pinned by digest. |
source | Upstream software is built from the upstream release tarball, verified by SHA-256 (and by signature where upstream signs) in the build stage. Version and checksum are build arguments that mirror commonlayer.yaml. |
multi-stage | Build tools exist only in build stages. The final image contains no compiler, no package manager, no curl or wget, and no debugging tools. |
non-root | Runs as UID/GID 10001:10001 (commonlayer). No USER root, no setuid binaries, no sudo or gosu. |
filesystem | Application files in /app (read-only), configuration in /etc/commonlayer/<app>, data in /var/lib/commonlayer/<app>, logs in /var/log/commonlayer/<app> (applications log to stdout/stderr by default), scratch in /tmp. |
read-only-rootfs | Works with a read-only root filesystem when the documented writable paths are mounted. |
entrypoint | /usr/local/bin/commonlayer-entrypoint: POSIX sh, set -eu, ends in exec. Supports <VAR>_FILE for every secret variable and never prints secrets. |
signals | The application is PID 1 after exec and shuts down gracefully on SIGTERM; STOPSIGNAL is set where upstream differs. |
healthcheck | A HEALTHCHECK using cl-probe or the application's own client. |
env | Variables are prefixed with the application name. Where upstream's official image has an established variable (for example POSTGRES_PASSWORD), the same name is supported to ease migration. |
labels | OCI annotations org.opencontainers.image.{title, description, version, revision, created, source, url, documentation, licenses, vendor, base.name, base.digest} and org.commonlayer.{project, variant, upstream.version, revision, standard}. |
platforms | linux/amd64 and linux/arm64, built on native runners and joined into one index. |
reproducibility | SOURCE_DATE_EPOCH is honoured and inputs are pinned. Documented as best effort, not claimed as bit-for-bit. |
licenses | Upstream license texts under /usr/share/licenses/; a third-party license inventory generated from the SBOM is published with each release. |
sbom-sign-provenance | SPDX and CycloneDX SBOMs, a cosign keyless signature and a SLSA v1 provenance attestation, all attached to the digest. |
Scope
The standard applies to every container image published by a CommonLayer project. It is checked in three places: the project's own tests (run locally and in CI), the scan gate in its release workflow, and the platform's verification policy, which checks among other things that the image user is not root, that the required labels are present, that every required platform is in the index, and that a signature, SBOM and provenance attestation from the registered identity exist for the digest.
A project's repository is also checked statically, without running anything: by the onboarding scan when it is connected through the CommonLayer GitHub App, and afterwards by the repository guardrails. Their image rules (image.non_root, image.base, image.healthcheck and others) refer to the identifiers below, and each finding names the Dockerfile line. See the project template and the scan.
The base image
The CommonLayer base image is built in two stages. An Alpine stage, pinned by digest, installs a minimal package set into an empty root and then removes the package manager; the final stage is FROM scratch plus that root. It contains musl, BusyBox with a reduced set of applets (the shell and core utilities entrypoints need; network clients such as wget, nc, telnet and ftp removed), CA certificates, time zone data, the commonlayer user and group with ID 10001, the standard directories with correct ownership, and cl-probe.
The package database is kept on purpose, so that scanners can identify every package. cl-probe is a small static program for health checks (http, tcp and file modes), which removes the need for curl or wget in images. The base image is itself a CommonLayer project with its own manifest, SBOM, signature, scans and a weekly rebuild.
Filesystem layout
/app- Application files, read-only.
/etc/commonlayer/<app>- Configuration.
/var/lib/commonlayer/<app>- Persistent data; mount a volume here.
/var/log/commonlayer/<app>- Log files, when an application cannot log to stdout/stderr.
/tmp- Scratch space; mount an emptyDir or tmpfs when the root filesystem is read-only.
Images run as UID 10001. When migrating from images that use another UID (Bitnami images use 1001, for example), existing volumes need their ownership adjusted; the migration guides explain how for each project.
Deviations
Some software cannot meet a rule as written. A project documents each deviation in its commonlayer.yaml as { standard: <identifier>, reason: <text> }; the identifier must be one of those above. Deviations are part of the project's public manifest. An undocumented deviation — for example an image whose user is root — fails verification and is not published.
What is not claimed
- No SLSA level is claimed beyond what the build actually satisfies. Provenance states what was built, from which commit, by which workflow.
- “No known vulnerabilities” describes the last scan, not the absence of vulnerabilities.
- Reproducibility is best effort, not a bit-for-bit guarantee.
- No CIS or FIPS conformance is claimed.
- Applying Apache-2.0 to a Dockerfile does not relicense the image's contents; each image keeps its upstream licenses and publishes an inventory.
Charts follow a companion Helm chart standard.