# Quickstart for Chainguard Containers

URL: https://chainguard-docs-preview-git-okta-scim-update.chainguard.app/chainguard/containers/quickstart.md
Last Modified: October 8, 2026
Tags: Chainguard Containers, Getting Started

An end-to-end walkthrough of Chainguard Containers: pull a free container, run a small Node.js application on it, and verify the image's signature and SBOM.


This quickstart covers one full cycle of working with a [Chainguard Container](/chainguard/containers/overview/): 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](https://images.chainguard.dev/).

These steps link to reference documentation instead of explaining each concept in place. Follow the links whenever you want additional details and context.

## Prerequisites

To follow this quickstart, you need:

* [Docker](https://docs.docker.com/engine/install/) or another OCI-compatible container runtime installed on your local machine.
* [Cosign](/open-source/sigstore/cosign/how-to-install-cosign/), which you use in Step 4 to verify a container image, installed.
* [jq](https://jqlang.github.io/jq/download/) installed. jq is a lightweight, command-line JSON processor; this guide uses it in Step 4 to make Cosign output more easily readable.

You don't need a Chainguard account. Every image in this guide is a [Free container](/chainguard/containers/concepts/container-categories/#free-containers): publicly available, with no authentication required. Production Containers, which add version-specific tags and patch SLAs, require [authenticating to the registry](/chainguard/containers/registry/authenticating/).

## Step 1: Pull and run a container

Pull the Node container from `cgr.dev`, Chainguard's registry:

```shell
docker pull cgr.dev/chainguard/node:latest
```

Chainguard 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:

```shell
docker run --rm cgr.dev/chainguard/node:latest --version
```

```output
v26.6.0
```

Your 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.

## Step 2: Build your application on the container

Create a directory for a demo application:

```shell
mkdir ~/hello-chainguard && cd ~/hello-chainguard
```

Create 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:

```shell
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"));
EOF
```

Then create a `Dockerfile` in the same directory:

```shell
cat > Dockerfile <<EOF
FROM cgr.dev/chainguard/node:latest

WORKDIR /app
COPY --chown=node:node server.js ./

EXPOSE 8080
CMD ["server.js"]
EOF
```

Chainguard's container images 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:

```shell
docker build . --pull -t hello-chainguard
```

Using the `hello-chainguard` image, start a container in the background:

```shell
docker run -d --name hello-cg -p 8080:8080 hello-chainguard
```

Then send it a request:

```shell
curl localhost:8080
```

```output
{"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:

```shell
docker rm -f hello-cg
```

## Step 3: Use a development variant when you need extra tooling

Chainguard's standard containers follow a [distroless](/chainguard/containers/concepts/getting-started-distroless/) philosophy: they carry only what the container needs to function. For example, the Node container has no system package manager:

```shell
docker run --rm --entrypoint sh cgr.dev/chainguard/node:latest -c "apk --version"
```

```output
sh: apk: not found
```

The development variant, tagged `latest-dev`, adds `apk` and other utilities for building, testing, and debugging:

```shell
docker run --rm --entrypoint sh cgr.dev/chainguard/node:latest-dev -c "apk --version"
```

```output
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](/chainguard/containers/concepts/container-variants/) for how the variants differ, and to [porting a sample application](/chainguard/containers/migration/porting-apps-to-chainguard/) for a multi-stage example.

## Step 4: Verify the container and inspect its SBOM

Chainguard signs every container it builds, along with the attestations that describe it. Check the signature on the image you pulled:

```shell
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 | jq
```

Cosign 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:

```shell
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](/chainguard/containers/security-and-compliance/verifying-chainguard-images-and-metadata-signatures-with-cosign/) and [retrieving SBOMs and attestations](/chainguard/containers/security-and-compliance/retrieve-image-sboms/).

## Next steps

* Work through a guide for your own stack: [nginx](/chainguard/containers/getting-started/web-and-data-services/nginx/), [PostgreSQL](/chainguard/containers/getting-started/web-and-data-services/postgres/), [Python](/chainguard/containers/getting-started/languages-and-runtimes/python/), [Go](/chainguard/containers/getting-started/languages-and-runtimes/go/), or [any other language or service](/chainguard/containers/getting-started/).
* Move an existing workload over with the [migration guides](/get-started/migration/).
* Learn what a Chainguard Container includes and who patches what in the [shared responsibility model](/chainguard/containers/concepts/shared-responsibility-model/) and the [container categories reference](/chainguard/containers/concepts/container-categories/).
* Understand why these images carry so few vulnerabilities in [Chainguard's low-to-no CVE commitment](/chainguard/containers/concepts/zerocve/).

