Chainguard Products Changelog
Weekly changelog of Chainguard product updates — product announcements, breaking changes, container images reaching …
For the complete documentation index, see llms.txt.
This quickstart covers one full cycle of working with a Chainguard Container: pull an image from Chainguard’s registry, run your own code on it, and verify where it came from. The example uses the Node container, but the same workflow applies to every image in the Chainguard Containers Directory.
These steps link to reference documentation instead of explaining each concept in place. Follow the links whenever you want additional details and context.
To follow this quickstart, you need:
You don’t need a Chainguard account. Every image in this guide is a Free Container: publicly available, with no authentication required. Production Containers, which add version-specific tags and patch SLAs, require authenticating to the registry.
Pull the Node container from cgr.dev, Chainguard’s registry:
docker pull cgr.dev/chainguard/node:latestChainguard publishes two tags for Free Containers — latest and latest-dev — and both point at the most recent build of the image.
The image’s entrypoint is node, so any arguments you pass go to the Node binary. Run the container, passing an argument to check the Node version:
docker run --rm cgr.dev/chainguard/node:latest --versionv26.6.0Your output may show a different version. Chainguard rebuilds its containers as upstream releases and package updates land, and the latest tag follows the newest build.
Create a directory for a demo application:
mkdir ~/hello-chainguard && cd ~/hello-chainguardCreate a file named server.js to hold an example JavaScript application. It uses only the Node standard library, so the application has no dependencies to install:
cat > server.js <<EOF
const http = require("node:http");
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({
message: "Hello from a Chainguard Container",
node: process.version,
uid: process.getuid(),
}));
});
server.listen(8080, () => console.log("Listening on port 8080"));
EOFThen create a Dockerfile in the same directory:
cat > Dockerfile <<EOF
FROM cgr.dev/chainguard/node:latest
WORKDIR /app
COPY --chown=node:node server.js ./
EXPOSE 8080
CMD ["server.js"]
EOFChainguard Containers run as a nonroot user by default — node in this image — so COPY --chown=node:node gives the application access to its own files without switching to root. Because the entrypoint is already node, CMD only needs to name the script.
Build the image:
docker build . --pull -t hello-chainguardUsing the hello-chainguard image, start a container in the background:
docker run -d --name hello-cg -p 8080:8080 hello-chainguardThen send it a request:
curl localhost:8080{"message":"Hello from a Chainguard Container","node":"v26.6.0","uid":65532}The uid in the response is 65532, confirming that the application runs unprivileged. Stop the container when you’re done:
docker rm -f hello-cgChainguard’s standard containers follow a distroless philosophy: they carry only what the container needs to function. For example, the Node container has no system package manager:
docker run --rm --entrypoint sh cgr.dev/chainguard/node:latest -c "apk --version"sh: apk: not foundThe development variant, tagged latest-dev, adds apk and other utilities for building, testing, and debugging:
docker run --rm --entrypoint sh cgr.dev/chainguard/node:latest-dev -c "apk --version"apk-tools 2.14.10, compiled for x86_64.To keep a small attack surface in production, install dependencies and compile artifacts in the development variant, then copy the results into the standard variant with a multi-stage build. Refer to Development and production container variants for how the variants differ, and to porting a sample application for a multi-stage example.
Chainguard signs every container it builds, along with the attestations that describe it. Check the signature on the image you pulled:
cosign verify \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity=https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main \
cgr.dev/chainguard/node | jqCosign confirms that the signature exists in the transparency log and that a trusted certificate authority issued the signing certificate, then prints the signature payload.
Every container also ships with a signed SBOM. Download the SPDX document to see each package in the image:
cosign download attestation \
--platform linux/amd64 \
--predicate-type https://spdx.dev/Document \
cgr.dev/chainguard/node | jq -r '.payload' | base64 -d | jq -r '.predicate'For the rest of the available attestations and the commands that verify them, refer to verifying containers and metadata signatures and retrieving SBOMs and attestations.
Last updated: 2026-08-05 00:00