CI runner images, built for Brik.
Pre-installed bash + yq + jq + git + your stack toolchain. Signed, attested, scanned, multi-arch.
Stop bootstrapping every CI job.
Official Docker images for Brik CI/CD runners.
Stock language images (node:24-slim, python:3.13-slim) are great for development. They are not built for CI:
- Every CI job re-installs the same
bash 5+,yq,jq,git,curl. 30 to 40 seconds, every job, every pipeline. - No signature, no attestation, no SBOM. You take the maintainer's word for what is inside.
- CVE patches lag. Stock images rebuild on the maintainer's cadence, not yours.
- No CI-specific tooling. SAST, SCA, secret scanning, container linting: you bring your own and install per job.
brik-images are CI runner images. Bootstrap is gone. Provenance is signed. CVE posture is published live, per image, on every push. Scanner and analysis tools come pre-installed in dedicated images.
Every image is signed with cosign (keyless, OIDC) and carries a SLSA build-provenance attestation you can verify with gh attestation verify. No supply chain trust gap. See Verifying for the exact commands.
Live CVE counts in the Available images table, not a static "0 vulnerabilities" badge. The build hard-fails only on a Critical CVE that has an available upstream fix. Everything else is recorded, visible in the Security tab, and treated as known posture rather than hidden risk. The full posture is documented in Current CVE posture.
Every build applies the base image's pending OS security updates (apt-get upgrade). Weekly rebuilds refresh base images. Dependabot auto-merges digest updates. Suppressed CVEs are reviewed every Monday by an automated CVE Suppression Review issue so nothing rots silently.
bash 5+, yq, jq, git, curl are pre-installed in every image. Stack images add their toolchain. The Brik runtime itself is cloned at CI time by the shared library's before_script, which decouples image releases from Brik releases.
Every image contains:
- bash (5.x)
- yq: YAML processor
- jq: JSON processor
- git: version control
- curl: HTTP client
Stack images additionally include their respective toolchain (node/npm, python/pip, java/maven, rust/cargo, dotnet/sdk). Exact pinned versions of every bundled tool are in versions.json.
The scanning tooling is split into two images based on their runtime requirements:
- analysis: Python/Ruby runtime, for deep SAST analysis, license compliance, and IaC scanning.
- scanner: static Go binaries only, fast to pull, for vulnerability scanning, secret detection, Dockerfile linting, and container scanning.
| Tool | Purpose |
|---|---|
| semgrep | Static analysis (SAST) |
| checkov | Infrastructure-as-Code scanning |
| scancode-toolkit | License and origin detection |
| license_finder | License compliance |
| Tool | Purpose |
|---|---|
| grype | Vulnerability scanning (SCA) |
| syft | SBOM generation |
| osv-scanner | Open-source vulnerability scanning |
| hadolint | Dockerfile linting |
| gitleaks | Secret/credential leak detection |
| trufflehog | Secret scanning (entropy + patterns) |
| dockle | Docker image best-practice linting |
Note
The Brik runtime itself is NOT pre-installed. It is cloned at CI time by the shared library's before_script. This decouples image releases from Brik releases.
brik-runner-deploy (FROM base) carries the CD toolchain used by the deploy-class runner in Brik's CD flow. It adds cosign and oras on top of the deploy tools so the flow can verify the signed attestation on the resolved digest before deploying.
| Tool | Purpose |
|---|---|
| helm | Kubernetes package manager |
| kubectl | Kubernetes CLI |
| argocd | GitOps CD controller CLI |
| cosign | Verify image signatures and attestations |
| oras | OCI artifact transport (evidence) |
All images are multi-arch: linux/amd64 and linux/arm64.
Every image is scanned, signed, and continuously rebuilt:
- ✅ Scanned with Grype on every build. The build hard-fails only on a Critical CVE that has an available upstream fix. Critical and High CVEs that upstream has not patched yet are recorded, not blocked.
- ✅ Scan results uploaded to the Security tab for full per-image visibility.
- ✅ SBOMs generated with Syft in CycloneDX format.
- ✅ Images signed with cosign (keyless, OIDC).
- ✅ Every build applies the base image's pending OS security updates; weekly rebuilds refresh the base images; Dependabot auto-merges digest updates.
Important
These images are not CVE-free. They bundle the latest upstream base images (node:*-slim, python:*-slim, Debian, Alpine), and those carry vulnerabilities their maintainers have not patched yet. Several stack images currently show Critical CVEs, and every non-base image shows dozens of High. The per-image badge in the Available images table is the live count, and the Security tab the per-CVE detail. Treat those, not this prose, as the source of truth.
What we control: the bundled tools (yq, jq, git, and the scanner/analysis binaries) are pinned to current releases and bumped regularly; the build blocks on a Critical that has a fix.
What we don't control: CVEs in the language runtimes themselves and in statically-linked Go binaries; those clear only when the runtime or tool upstream ships a patched release. For production use, pin images by digest and run a scan gate against your own risk policy.
Important
A few CVEs are suppressed in .grype.yaml so the build can stay green: Go-toolchain vulnerabilities compiled into statically-linked scanner binaries (dockle, gitleaks, osv-scanner) that we cannot remediate without forking the upstream project. .grype.yaml is the canonical list.
The live status (the per-tool breakdown, and whether a patched upstream release is available yet) is the auto-refreshed CVE Suppression Review issue, regenerated every Monday by scripts/review-cve-suppressions.sh. When a tool ships a release on a patched Go, bump it in versions.json and drop its entry from .grype.yaml and .cve-suppressions.json.
Every image is signed with cosign (keyless) and carries a SLSA build-provenance attestation. To verify a pulled image yourself (adjust the tag as needed):
# cosign signature
cosign verify ghcr.io/getbrik/brik-runner-node:24 \
--certificate-identity-regexp 'https://github.com/getbrik/brik-images/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
# GitHub build-provenance attestation
gh attestation verify oci://ghcr.io/getbrik/brik-runner-node:24 --owner getbrikEvery attestation is also listed on the Attestations page.
Each image is published with multiple tags:
ghcr.io/getbrik/brik-runner-node:22 # stack version (mutable)
ghcr.io/getbrik/brik-runner-node:latest # latest LTS (mutable)
ghcr.io/getbrik/brik-runner-node:sha-a1b2c3d # immutable git SHA
ghcr.io/getbrik/brik-runner-node:22@sha256:... # digest pin (most secure)
Important
For production pipelines, pin images by digest (@sha256:...) to guarantee reproducible builds. Mutable tags like :22 or :latest can change on rebuilds. Use docker inspect --format='{{index .RepoDigests 0}}' <image> to retrieve the current digest.
You do not pick the image by hand, and you do not install brik into it. The init job
reads project.stack and project.stack_version from your brik.yml and resolves each
stage to the matching ghcr.io/getbrik/brik-runner-<stack>:<version>; the shared library
clones the Brik runtime into the container at job start.
Include the Brik template. That is the whole pipeline:
# .gitlab-ci.yml
include:
- project: 'brik/gitlab-templates'
ref: v0.7.0
file: '/templates/brik-integrate.yml'With project.stack: node and stack_version: "22" in brik.yml, the stage jobs run on
ghcr.io/getbrik/brik-runner-node:22; the scan, analysis and deploy stages run on their
dedicated images automatically.
Load the Brik shared library and call the orchestrator. Same automatic image resolution
from brik.yml:
// Jenkinsfile
@Library('brik') _
brikIntegrate()With the Brik CLI installed on your host, run the flow straight from your project directory. Brik drives the same containerized engine as CI, spawning one brik-runner container per stage (the image is resolved from your brik.yml):
brik integrate # full CI flow locally, one container per stage
brik stage build # or run a single stage in its runner imageEach runner class maps to an image in a runner-class map. To pull from a mirror, an
air-gapped registry, or your own runners extended with in-house tooling, point Brik at an
alternate map with the BRIK_RUNNER_CLASSES_FILE pipeline variable (a CI variable on
GitLab, a build parameter on Jenkins) instead of editing your CI file:
# my-runner-classes.yml, committed in your repo
classes:
base: { image: registry.example.com/acme/brik-runner-base, tag: "1.4.0" }
scanner: { image: registry.example.com/acme/brik-runner-scanner, tag: "1.4.0" }
analysis: { image: registry.example.com/acme/brik-runner-analysis, tag: "1.4.0" }
deploy: { image: registry.example.com/acme/brik-runner-deploy, tag: "1.4.0" }
stack: { image_env: BRIK_CI_IMAGE } # stack image stays resolved from brik.ymlThen set BRIK_RUNNER_CLASSES_FILE=my-runner-classes.yml for the pipeline.
Note
Native pipeline templates exist for GitLab and Jenkins. The same containerized engine also runs on any host with Docker, including a CI runner with no native Brik adapter such as GitHub Actions: install the CLI and call brik integrate. The only thing still on the Roadmap is the zero-config form, docker run <brik-runner-image> brik ..., with Brik baked into the image so no host install is needed.
# Build all images (multi-arch, no push)
./scripts/build-local.sh
# Build and load into local Docker (native arch only)
./scripts/build-local.sh --load
# Build specific stacks (expands to all versions)
./scripts/build-local.sh --load node python
# Build specific targets
./scripts/build-local.sh --load analysis-1 scanner-1| Option | Description |
|---|---|
| (no args) | Build all images (multi-arch) |
<stack> |
Build all versions of a stack (e.g. node builds node-22 + node-24) |
<target> |
Build a specific target (e.g. node-22, quality-1) |
--load |
Load images into local Docker (forces native arch) |
--platform PLAT |
Override platforms (e.g. linux/amd64) |
--no-cache |
Disable Docker build cache |
--regenerate |
Regenerate docker-bake.hcl before building |
--push |
Push images to registry (requires authentication) |
--list |
List all available targets and stacks |
--dry-run |
Show the command without executing it |
# List available targets
./scripts/build-local.sh --list
# Rebuild analysis image from scratch, single arch
./scripts/build-local.sh --load --no-cache analysis-1
# Build for a specific platform
./scripts/build-local.sh --platform linux/amd64 scanner-1
# Regenerate bake file and build everything
./scripts/build-local.sh --regenerate --load
# Preview the command without running it
./scripts/build-local.sh --dry-run node java# Generate the bake file from the version matrix
./scripts/generate-bake.sh
# Run smoke tests on built images
./scripts/smoke-test.sh
# Lint Dockerfiles
hadolint images/*/DockerfileAll tool and stack versions are defined in versions.json (single source of truth). To add or update a version:
- Edit
versions.json - Run
./scripts/generate-bake.sh(or use--regeneratewithbuild-local.sh) - Commit and push; CI handles the rest
The Brik runtime is currently cloned at run time (by the GitLab and Jenkins adapters, and by the local engine) rather than baked into the image, keeping image releases decoupled from Brik development. So today you run Brik with the CLI installed on your host (see Local). Once Brik reaches a stable release cadence, the runtime will be pre-installed in the images, unlocking zero-config use with no host install (docker run ghcr.io/getbrik/brik-runner-node:22 brik stage build) and fully offline pipelines.
MIT
