Skip to content

build/bake: BuildKit secrets support#248

Open
crazy-max wants to merge 1 commit into
docker:mainfrom
crazy-max:buildkit-secrets
Open

build/bake: BuildKit secrets support#248
crazy-max wants to merge 1 commit into
docker:mainfrom
crazy-max:buildkit-secrets

Conversation

@crazy-max

@crazy-max crazy-max commented Jul 7, 2026

Copy link
Copy Markdown
Member

fixes #40
closes #239

This adds BuildKit secret support to the build and bake reusable workflows through a shared build-secrets workflow secret.

The new build-secrets secret accepts a YAML object that maps BuildKit secret IDs to secret values. Secret IDs may contain letters, digits, underscores, and dashes. The build workflow parses that YAML and forwards each entry to docker/build-push-action through secret-envs.

The bake workflow uses the same unqualified key contract and scopes those entries to the workflow target input. It also accepts Bake-only target-scoped keys in the form target.secret_id, which appends target.secrets+=id=...,env=... overrides for docker/bake-action instead of using a broad wildcard override. This replaces existing same-ID Bake secret sources when they are already declared for that target.

A caller can pass a literal multiline secret with YAML block syntax.

secrets:
  build-secrets: |
    npmrc: |
      //registry.npmjs.org/:_authToken=token
      always-auth=true

A caller can also pass GitHub secrets through toJSON(...) so multiline values and YAML-sensitive characters are preserved.

secrets:
  build-secrets: |
    npmrc: ${{ toJSON(secrets.NPMRC) }}
    release.aws_credentials: ${{ toJSON(secrets.AWS_CREDENTIALS) }}

Reusable workflows cannot accept arbitrary dynamic secret names, so this keeps the public contract stable while still allowing callers to provide any BuildKit secret ID they need.

The workflow does not accept file paths as secret payloads. Bake targets can still declare file-based secret sources for local docker buildx bake usage, and matching build-secrets entries override those sources with env-backed workflow-provided values during the reusable workflow run.

@crazy-max crazy-max marked this pull request as ready for review July 7, 2026 11:02
@crazy-max crazy-max requested a review from a team as a code owner July 7, 2026 11:02
@crazy-max crazy-max requested a review from tonistiigi July 7, 2026 11:02
@crazy-max crazy-max mentioned this pull request Jul 7, 2026
Comment thread .github/workflows/build.yml Outdated
envs[envName] = secret;
secretEnvs.push(`${id}=${envName}`);
});
core.setOutput('envs', JSON.stringify(envs));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these setOutput calls really safe?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this so secret-bearing env values are exported with core.exportVariable(...) instead of being serialized through core.setOutput('envs', ...). The later env: ${{ fromJson(steps.prepare.outputs.envs || '{}') }} plumbing is gone from both build steps.

secret-envs remains an output in build.yml, but that only contains secret_id=ENV_NAME mappings, not secret values.

Comment thread .github/workflows/bake.yml Outdated
Object.entries(buildSecrets).forEach(([id, secret], index) => {
const envName = toBuildSecretEnvName(id, index);
envs[envName] = secret;
secretOverrides.push(`*.secrets+=id=${id},env=${envName}`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this adding secrets to the definition? I would think secrets need to be defined already and just values are loaded here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dug into this more and I think the secrets+= override is intentional here.

For Bake, target.secret defines both the BuildKit secret ID and the source for that secret. If a caller has secret = ["id=foo,env=MY_FOO"], only exporting another env var from the workflow does not change what Bake uses. Using *.secrets+=id=foo,env=<internal env> replaces the existing same-ID secret source instead of duplicating it, which I verified with buildx bake --print.

That keeps the reusable workflow contract as build-secrets: { foo: value } and avoids making callers reference github-builder internal env names in their Bake files. It also lets existing Bake targets keep local sources for direct docker buildx bake usage, while the reusable workflow overrides those sources when a matching build-secrets entry is provided.

I updated the docs to avoid saying file-based secrets are supported as workflow payloads. The workflow still accepts secret values only and exposes them to BuildKit from env vars. A Bake target can still declare a file source for local use, for example type=file,id=aws,src=${HOME}/.aws/credentials, and the reusable workflow overrides that source when build-secrets contains aws.

Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/build.yml
Comment thread .github/workflows/bake.yml Outdated
@crazy-max crazy-max requested a review from tonistiigi July 9, 2026 10:32
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Secrets support

2 participants