Deploy — Docker
Builds a multi-platform Docker image and pushes it to GitHub Container Registry (GHCR).
Usage
yaml
jobs:
docker:
uses: KevinDeBenedetti/github-workflows/.github/workflows/cd-docker.yml@main
with:
image-name: my-api
platforms: linux/amd64,linux/arm64Inputs
| Input | Type | Default | Description |
|---|---|---|---|
image-name | string | '' | Image name (e.g. my-api). Defaults to the repository name. |
context | string | '.' | Docker build context (e.g. apps/api) |
dockerfile | string | Dockerfile | Path to the Dockerfile, relative to context |
platforms | string | linux/amd64,linux/arm64 | Comma-separated target platforms |
push | boolean | true | Push image to GHCR |
target | string | '' | Build target stage (e.g. dev, prod) |
tag-latest | boolean | false | Also tag the image as :latest on the default branch |
version | string | '' | Explicit version tag to apply (e.g. v1.2.3). When set, adds a type=raw tag with this value. |
build-args | string | '' | List of Docker build-args (one per line, KEY=VALUE). Never secrets — see below. |
provenance | string | mode=max | SLSA provenance attached by BuildKit (mode=max, mode=min, false) |
sbom | boolean | true | Attach an SBOM attestation to the image |
attest | boolean | true | Sign a GitHub artifact attestation for the pushed image (only when push) |
timeout-minutes | number | 60 | Minutes before the build job is cancelled (GitHub's default is 360) — raise it for a slow emulated build |
Secrets
| Secret | Required | Description |
|---|---|---|
BUILD_SECRETS | No | BuildKit secrets, one id=value per line, read in the Dockerfile with RUN --mount=type=secret,id=<id> |
Outputs
| Output | Description |
|---|---|
image | Image tag(s) applied to the build (newline-separated) |
digest | Image digest |
Tags applied
| Event | Tag |
|---|---|
| Branch push | <branch-name> |
| Pull request | pr-<number> |
| Semver tag | <version>, <major>.<minor> |
| Any commit | sha-<short-sha> |
Default branch + tag-latest: true | latest |
Notes
- Uses
docker/metadata-actionfor automatic tag and label generation. - Build cache is scoped per
contextusing GitHub Actions cache (type=gha). - QEMU is registered only when
platformsnames something other thanlinux/amd64, the runner's own architecture. Anarm64image built this way runs under emulation, several times slower than a native build: for a large image, build each platform on its own runner (GitHub'subuntu-24.04-armlabel for the arm64 half) and merge the manifests, as Docker documents, or droplinux/arm64fromplatformswhen nothing deploys on it. timeout-minutescuts off a build stuck on a pull or an emulated step: without it the job holds the runner (and bills the minutes) for up to 6 hours.- Each build adds a
sha-*tag, and each platform image and attestation is an untagged version in GHCR:cleanup-ghcrdeletes the old ones on a schedule without breaking multi-arch images. - The job runs in the
productionenvironment onmain, otherwisepreview. - Caller permissions — a reusable workflow gets at most what its caller grants, and GitHub refuses the run if a job asks for more (even when
attestis false). Grant on the calling job:yamlpermissions: contents: read packages: write deployments: write id-token: write # attestation signing attestations: write - Never pass secrets in
build-args: they stay in the image history, andprovenance: mode=maxrecords their values in an attestation anyone pulling the image can read. UseBUILD_SECRETS(secret mounts) instead. - The attestation is signed in a separate job, so the build step (which runs the Dockerfile) never holds an OIDC token. Verify an image with
gh attestation verify oci://ghcr.io/<owner>/<image>@<digest> --owner <owner>.