Chainguard + Renovate + GitLab CI demo
Chainguard images, kept current by Renovate
This page is served by a Node.js app running on a Chainguard node image in an AWS Lambda function. Renovate runs in GitLab CI and keeps that image up to date, and every update is tested in a merge request before it can ship.
Running right now
- Commit
- 4c384a28
- Committed
- 2026-11-12 03:42 UTC
- Deployed by pipeline
- #3057505716
- Base image
- node:26
- Base image digest
unknown- Node.js
- v26.13.0
- Express
- 5.3.0
- npm packages (runtime + build)
- 78 of 132 built by Chainguard, 54 via upstream fallback
The same data as JSON: /version. The deploy pipeline checks that endpoint after every release and fails if it doesn't report the new commit.
How it works
Press play to follow a new Chainguard image digest from cgr.dev to this page, click any box for its configuration, and switch the options to see other ways to run the same flow. Values marked live come from this deployment; the page doesn't call GitLab or anything else.
Chainguard rebuilds the image
Chainguard publishes new builds of
node:26as fixes land upstream. The tag stays the same, but its digest changes.Renovate opens one merge request
A scheduled GitLab pipeline runs Renovate. It sees the new digest and opens a merge request, or refreshes the one already open, so updates never pile up.
The test gate runs
The merge request pipeline builds the image with the new base and runs the unit tests on top of it. GitLab won't allow the merge unless it passes.
Merge, deploy, verify
On merge, the main pipeline pushes the tested image, deploys it by digest, and smoke tests this site.
The pieces
Minimal, frequently rebuilt base images
The build uses node:26-dev, which has npm and build tools. The app runs on the much smaller node:26 image as a non-root user. Both are pinned by digest in the Dockerfile.
npm packages built from source
Express (the server) and React, React Flow and Vite (this diagram's build) are installed from libraries.cgr.dev instead of the public npm registry. The lockfile records where each package came from, which is what the count above is based on. Packages Chainguard hasn't built yet come through Libraries' upstream fallback and are counted separately.
Digest-only updates, one MR
Self-hosted Renovate is configured to only move image digests (like digestabot) and to group them, so there is never more than one Renovate merge request open.
Three jobs
renovate runs on a schedule, test runs on merge requests, and deploy runs on main. Every CI image and tool is pinned by version and digest or checksum.
An Event Function from a container image
The image itself is the runtime. The AWS Lambda Web Adapter turns Lambda invocations into ordinary HTTP requests to Express, so the app has no Lambda-specific code.
No stored Chainguard or AWS secrets
Each CI job asks GitLab for a short-lived OIDC token and exchanges it for the access it needs:
- Chainguard: every job (renovate, test and deploy) assumes one shared Chainguard identity. It trusts tokens from any branch of this one project and can only pull: images from cgr.dev and npm packages from Chainguard Libraries.
- AWS: the deploy job also assumes an AWS IAM role that trusts only
mainof this project, and can only push to its ECR repository and update its Lambda function.
Explore
- Repository
- README (setup runbook)
- Implementation guide
- .gitlab-ci.yml
- renovate.json5
- Dockerfile
- Merge requests
Need access to the repository, or want to see this set up for your team? Email michael.miara@chainguard.dev.