Public image set

Hardened container images.
Minimal, signed, verifiable.

42 minimal, non-root images on musl and glibc. Each is signed with cosign and ships with a CycloneDX SBOM and SLSA provenance, so you can check what you pull before it runs.

CONNECTED SECURITY RECORDCODE TO RUNTIME
Hardened images
Code to runtime security path A connected path from source and scanning, through the build artifact, to the running workload and governance decision. CODE SOURCE + SCAN ARTIFACT BUILD + IMAGE + GATE RUNTIME WORKLOAD + RESPONSE
SourceArtifactRuntimeDecision
42
Images
9 families, each on musl (Alpine 3.23) and glibc (Ubuntu 24.04).
65532
Non-root
Every image runs as the nonroot user, never as root.
Digest
Reproducible
Built twice from one package snapshot; the two digests must match.
cosign
Signed
Recorded in the Rekor transparency log, with SBOM and provenance attestations.
The set

What are the SecurityVault hardened images?

Base, language runtime and service images built to carry your workload and little else. Our own builder copies the program and the exact shared libraries it loads, not a whole distribution.

Variants
musl images are based on Alpine 3.23. glibc images are based on Ubuntu 24.04 and carry a -glibc suffix in the tag.
Runtime images
The program, the shared libraries it loads, CA certificates, tzdata and the nonroot user. No shell, no package manager and no setuid or setgid files. Every distribution file keeps its package record, so scanners see every package.
-dev images
A runtime image plus a shell, core utilities and the distribution's package manager (busybox and apk on Alpine; Ubuntu's essential set and apt on glibc). They use the same package snapshot as their runtime twin, still run as 65532 and still hold no setuid or setgid file. Use them in build stages and for debugging, then copy the result into the runtime image. Compilers are not included.
Go images
The go images are build images by design: the toolchain, a shell and a package manager are included, and there is no separate -dev twin.
Non-root
Every image is configured to run as 65532:65532.
Reproducible
The build first makes a package snapshot pinned by digest, then builds the image from it twice and must reach the same digest. The snapshot is the only package source the image build sees, so the same image rebuilds on a later day.
Rebuilt daily
The build runs every day and checks each image against the scan gate again. A newly built set reaches the registry only after it has passed validation, so tags are rolling: pin the digest in production.
Catalog

Which images and tags are in the catalog?

Pull any tag as ghcr.io/securityvault-io/<image>:<tag>. 42 tags in 9 families.

ImageWhat it ismusl tags (Alpine 3.23)glibc tags (Ubuntu 24.04)Flavours
staticCA bundle, tzdata and the nonroot user, for statically linked binaries.latest latest-glibc Runtime
baseC library, libgcc and libstdc++ for dynamically linked programs.latest latest-dev latest-glibc latest-glibc-dev Runtime and dev
pythonCPython 3.13 and 3.12. The dev flavour adds pip.3.13 3.12 3.13-dev 3.12-dev 3.13-glibc 3.12-glibc 3.13-glibc-dev 3.12-glibc-dev Runtime and dev
nodeNode.js 22 and 24. Runtime images carry no npm; the dev flavour adds npm, npx and corepack.22 24 22-dev 24-dev 22-glibc 24-glibc 22-glibc-dev 24-glibc-dev Runtime and dev
openjdkHeadless Temurin 21: a JRE in the runtime image, a JDK in the dev flavour.21 21-dev 21-glibc 21-glibc-dev Runtime and dev
nginxnginx 1.30 on port 8080, with logs to stdout and stderr.1.30 1.30-dev 1.30-glibc 1.30-glibc-dev Runtime and dev
valkeyValkey 9.0 built from source, with upstream defaults including protected mode.9.0 9.0-dev 9.0-glibc 9.0-glibc-dev Runtime and dev
postgresPostgreSQL 17, server only. Initialise the data directory with the dev flavour first.17 17-dev 17-glibc 17-glibc-dev Runtime and dev
goGo 1.27 and 1.26 toolchain images, with a shell and package manager by design.1.27 1.26 1.27-glibc 1.26-glibc Build image
Verify

How do I verify an image before I pull it?

Every step uses the public key alone, with cosign 2.2 or later. The SBOM and provenance commands also need jq. No SecurityVault account is needed.

1 · Get the key

Download the public key. The same key is printed below so you can compare it with the file.

curl -fsSL https://securityvault.io/hardened-images/cosign.pub -o securityvault-hardened.pub
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhLRqA4eX3I656dkcsxO7O47RZejM
7p481ZVz1zgCSo9ZL8c+cppsIVG6GkOAThoepzd0eHeaYCHu31yUi+yQJg==
-----END PUBLIC KEY-----
2 · Check the signature

This succeeds only if the image behind the tag carries a signature from that key. Every signature and attestation is recorded in the public Rekor transparency log, and cosign verify also checks the inclusion proof.

cosign verify --key securityvault-hardened.pub ghcr.io/securityvault-io/python:3.13
3 · Read the SBOM

The CycloneDX SBOM is attached as a signed attestation. This verifies it and prints every component with its licence.

cosign verify-attestation --key securityvault-hardened.pub --type cyclonedx \
    ghcr.io/securityvault-io/python:3.13 | jq -r .payload | base64 -d | jq .predicate
4 · Read the provenance

The SLSA v1 provenance records the catalog entry, the source revision, the package snapshot and builder image the image was built from, and the digest of the result.

cosign verify-attestation --key securityvault-hardened.pub --type slsaprovenance1 \
    ghcr.io/securityvault-io/python:3.13 | jq -r .payload | base64 -d | jq .predicate
5 · Pin the digest
Tags move. Use the digest that cosign verify reports and reference the image as ghcr.io/securityvault-io/python@sha256:<digest> in your Dockerfiles and manifests.
Scan gate

What does the scan gate enforce?

Every image is scanned from its CycloneDX SBOM and directly with a second scanner before it can be published. An approved, time-limited OpenVEX statement may cover a finding for its exact component version and image.

Fails on
For findings outside an approved OpenVEX statement: any Critical, High or Unknown severity, or any available fix. Ignored matches and missing required SBOM components also fail the gate.
Passes with
Medium, Low and Negligible findings while no fix exists, and findings covered by a current, approved OpenVEX statement.
OpenVEX scope
A security owner must approve a not_affected statement naming the exact component version and image. Approval must be no more than 60 days old. Drafts exempt nothing; an expired approval fails the gate. An applicable statement is published as a signed openvex attestation. Verify it with cosign verify-attestation and --type openvex, using the published public key.
Release lines
An image pins its main runtime (Python, Node.js, OpenJDK, nginx, Valkey, PostgreSQL or Go) to a release line. A finding whose only fix is in a different release line is not a fix that line can take, so the gate records it as a note instead of holding the image.
Re-evaluated
The gate runs again on every daily build, against a fresh vulnerability database. When a fix ships for a finding that was admitted because none existed, the image is held until a new package snapshot carries the fix. An image that fails is not published, and the other images still are.
What it does not mean
Passing the gate does not mean an image is free of vulnerabilities. Findings with no fix available can remain, and scanner databases lag behind disclosures. Scan the SBOM yourself with the tool you trust.
Validation

How is an image set validated before it is published?

Each digest set passes two rounds and a security-owner sign-off. A newly built set needs its own validation record before it is published. SecurityVault runs both rounds; no third party has audited the set.

Round 1 · pipeline
Unit tests of the build tooling. The package snapshot, then the image built from it twice in separate build environments, with the digests compared. A CycloneDX SBOM, a vulnerability scan and the gate above. A failure stops that image.
Round 2 · separate
Run outside the build pipeline, against the registry digests. The image runs as 65532:65532 and names its package snapshot. No setuid, setgid or world-writable file apart from a sticky /tmp. No shell in runtime images. No build-environment content under /etc. The licence files are present. A second scanner applies the same policy. A rebuild with no cache from the recorded package snapshot must reproduce the same digest.
What round 2 found
On the first full pass, round 2 found two defects that round 1 had missed: a build sandbox's hosts file inside some images, and Python packages vendored inside pip that the SBOM did not list. Both were fixed and the set was rebuilt before anything was published. The pipeline gate uses one scanner over the SBOM; the second scanner runs in round 2.
Sign-off
A security owner approves the digest set. The publish run then refuses any other set, re-checks the gate for each image, copies the digests unchanged to the registry, signs them and attests the SBOM and provenance.
SLSA level
Build L1. The pipeline produces SLSA v1 provenance and attests it with a key it holds. L1 means provenance exists and describes the build; it makes no claim that the build platform resists tampering. We claim no higher level.
Licences

What are the licence terms?

Build definitions
The build definitions, scripts and builder are proprietary to SecurityVault and stay private. Only the images are published. Pulling an image costs nothing and needs no SecurityVault account.
Third-party software
Every image contains third-party software under its own licence. The CycloneDX SBOM lists each component with its licence, and the licences travel inside the image at the paths below. For Alpine packages the image holds the licence identifiers; the texts are in the Alpine aports tree at the versions the SBOM records.
  • Ubuntu packages (glibc images)/usr/share/doc/<package>/copyright
  • Alpine package database (musl images)/lib/apk/db/installed
  • Python/usr/local/lib/python3.X/LICENSE.txt
  • Node.js/usr/local/LICENSE
  • Eclipse Temurin/opt/java/openjdk/legal/
  • Go/usr/local/go/LICENSE
  • Valkey/usr/local/share/licenses/valkey/COPYING
  • nginx (musl images)/usr/share/licenses/nginx/COPYRIGHT
  • nginx (glibc images)/usr/share/doc/nginx/copyright
GPL and LGPL
Some components are under the GPL or LGPL, for example glibc, and busybox, apk-tools, apt and dpkg in the -dev images. We ship them unmodified. The corresponding source is the Alpine aports and Ubuntu source packages at the versions the SBOM records, and we will also supply it on request to hello@securityvault.io for three years from an image's publication.
Availability

Where are the images published?

Packages are published to GHCR and made public per image. A denied anonymous pull means that package is not public yet.

Registry

GitHub Container Registry, under ghcr.io/securityvault-io. Verify the image with the steps above before you use it.

docker pull ghcr.io/securityvault-io/python:3.13
FAQ

Hardened image questions

Are the hardened images free of vulnerabilities?
No. The scan gate blocks Critical, High and Unknown findings and available fixes, except where a current, security-owner-approved OpenVEX not_affected statement covers the exact component version and image. Drafts exempt nothing; expired approvals fail the gate. Medium, Low and Negligible findings without a fix can remain, and scanner databases lag behind disclosures. Every image ships a CycloneDX SBOM and, when a statement applies, a signed OpenVEX attestation, so you can check the evidence with your own tools.
Should I use the musl or the glibc images?
Use the variant your software is built and tested against. musl images are based on Alpine 3.23 and glibc images on Ubuntu 24.04; glibc tags carry a -glibc suffix. If your binaries need glibc, choose a glibc image.
Why does a runtime image have no shell?
Nothing in the image can run an interactive command or install a package, which removes tools an intruder would use. For build stages and debugging, use the -dev twin, which adds a shell, core utilities and the package manager, and copy the result into the runtime image. PostgreSQL needs one extra step: initialise the data directory with the -dev image first.
Do I need a SecurityVault account to pull or verify an image?
No. Pulling needs only the registry, and verification needs only the public key and cosign 2.2 or later.
Which SLSA level do the images reach?
Build L1. The pipeline produces SLSA v1 provenance and attests it with a key it holds. We claim no higher level.
Can I rebuild an image myself?
No. The build definitions are proprietary and the build repository is private. We build each image twice from one package snapshot and require the same digest, and a second, separate rebuild from the recorded snapshot must reproduce that digest before a set is published. The provenance attestation records the package snapshot and builder image digests.
Do the tags change?
Yes. The tags are rolling and a new validated digest set moves them. Pin the digest that cosign verify reports in production. Each published tag also gets a dated tag in the form <tag>-<yyyymmdd>.
Does SecurityVault scan images in my own registry?
Yes, as part of the platform. See container security for digest-pinned scanning, signature checks and image-to-workload binding.
Questions

Ask us about the image set.

Ask about the build process, the licence texts, or how SecurityVault scans images in your own registry.