Run GitHub Actions workflows as native Buildkite jobs without creating a GitHub Actions run.
buildkite-gha turns each supported workflow job and static matrix entry into a Buildkite job. Steps run in a compatibility runtime inside that job. Buildkite owns scheduling, logs, retries, cancellation, and the build UI.
Important
buildkite-gha is an experimental pre-1.0 preview. The released plugin path supports Linux x86-64 and native macOS arm64. The production path supports local and public actions, static Buildkite job-accessible secrets, and narrowly scoped, job-bound checkout, GITHUB_TOKEN, OIDC, artifact, and cache integrations. Private actions and GitHub-issued OIDC claims are unsupported.
Buildkite creates the build. The plugin reads the workload from the workflow file and dynamically uploads the jobs it supports.
| GitHub Actions | Buildkite |
|---|---|
Triggers and filters under on: |
Select applicable workflow groups inside an existing Buildkite build |
| Workflow run | Existing Buildkite build |
| Job | Buildkite command job |
| Matrix entry | Buildkite command job |
needs |
depends_on with verified result transport |
| Step | Runs inside the job compatibility runtime |
runs-on |
Supported platform label; Buildkite queue mapping chooses the agent |
Steps stay together because they share a workspace, environment changes, action state, and post-action cleanup. Buildkite still creates the build; buildkite-gha selects top-level workflows for its effective GitHub event before compiling them. Local workflow_call remains available for composition without creating its own group.
Add the GitHub Actions Buildkite plugin to your pipeline:
steps:
- label: ":github: Test"
key: "gha-ci"
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.yml
- label: ":rocket: Deploy"
depends_on: "gha-ci"
command: .buildkite/deploy.shThe plugin is a thin wrapper around the hidden buildkite-gha plugin entrypoint. It uses mise to install and verify the selected CLI release, and defaults to the latest stable release. During the preview, leaving version unset means there is no CLI version to update as new stable releases ship. Set workflow to one explicit path or workflows to an explicit path list; plugin configuration does not accept directories or glob patterns.
For on.release, open the pipeline's GitHub settings, select Additional Webhooks > Releases, and use Code trigger mode. Only explicit types containing published, created, and/or released are supported.
The importer can run on Linux x86-64 or native macOS arm64. Its agent targeting
is independent of runners: each runner mapping selects the queue for generated
workflow jobs, not the importer step.
To hold the CLI at a specific release instead, set version to an exact stable release from 0.9.0 onward:
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.yml
version: "0.10.1"Runtime v0.9.0 adds runner.os and runner.arch. They resolve to Linux and
X64 on Linux and macOS and ARM64 on native macOS. You can configure a
fallback queue for a macOS runner label:
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.yml
runners:
- runs-on: ubuntu-latest
queue: hosted
- runs-on: macos-14
queue: macos-sonoma-arm64Hosted runner labels are case-insensitive. Linux labels use the matching Noble
or Jammy hosted-toolchains image, with or without a configured queue.
During upload, the importer asks the job-scoped Agent API to resolve each
runs-on selector before using configured mappings or the local preset. A macOS
label selects native Darwin/arm64, not a GitHub image or Xcode inventory.
The imported workflows are a dynamic part of the Buildkite pipeline. The plugin creates one aggregate group per successfully compiled, explicitly listed workflow in a single transaction. Workflows that do not declare the selected event become top-level skipped steps. Groups and replacement steps depend on the importer. Each runnable job publishes a provider check named <workflow> / <job> (<event>): a GitHub check for GitHub events or an Origin check for Origin events. This approach lets you keep existing workflows while moving jobs to native Buildkite steps over time.
Buildkite owns build creation and schedule configuration. Within that build, buildkite-gha maps push, pull request, merge queue, release, manual/API, and scheduled builds to push, pull_request, merge_group, release, workflow_dispatch, and schedule, then applies the matching on: branch, tag, base-branch, activity, and safely evidenced path filters. Cross-event workflows are excluded before event-dependent compilation and retained as top-level skipped steps.
The compatibility reference is the source of truth. Use this table for a quick assessment:
| Good fit | Not currently supported |
|---|---|
Linux x86-64 and native macOS arm64 jobs using bash or sh |
Windows, Linux arm64, or macOS x86-64 |
| Local and public JavaScript and composite actions; verified Dockerfile actions on Linux | Private actions, Dockerfile actions on macOS, or arbitrary reusable-workflow source |
Static matrices, needs, outputs, and local reusable workflows |
Dynamic matrices and expressions outside the documented subset |
| Exact-commit checkout, including managed private repository access | GitHub environment secrets, GitHub-issued OIDC claims, or protected queues |
| Static Buildkite job-accessible secrets | Dynamic or reusable-workflow secret forwarding |
Scoped GITHUB_TOKEN and step github.token use allowed by Buildkite policy |
Ambient token injection or dynamic token access |
| Buildkite OIDC tokens through host JavaScript and composite actions | OIDC in Docker actions or job containers |
| Audited artifact action versions and cache v6 integration | Other artifact and cache modes or general GitHub service emulation |
| Background, wait, cancellation, and parallel step controls; Linux job and service containers | Implicit GHCR authentication, container hooks, or any Docker use on macOS |
Some features support a limited subset or behave differently on Buildkite. Check the matrix before migrating a workflow.
Imported workflows receive Buildkite-issued OIDC tokens, not GitHub-issued
tokens. An AWS role that trusts only GitHub's issuer or matches GitHub's sub
claim rejects them. To use an existing role from both systems:
- Register
https://agent.buildkite.comas another IAM OIDC provider with audiencests.amazonaws.com. - Add a separate trust-policy statement for the provider ARN
arn:aws:iam::AWS_ACCOUNT_ID:oidc-provider/agent.buildkite.com. - Match
agent.buildkite.com:audandagent.buildkite.com:subinstead of the equivalenttoken.actions.githubusercontent.comcondition keys. Scope the Buildkite subject to the intended organization and pipeline. - Keep the existing GitHub provider statement while workflows run in both systems. Remove it only when nothing still uses GitHub-issued tokens.
For example, the Buildkite statement can use these conditions:
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::AWS_ACCOUNT_ID:oidc-provider/agent.buildkite.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"agent.buildkite.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"agent.buildkite.com:sub": "organization:ORGANIZATION_SLUG:pipeline:PIPELINE_SLUG:*"
}
}
}The workflow must grant id-token: write. The endpoint is available to host
JavaScript and composite actions, including aws-actions/configure-aws-credentials.
Use the plugin's oidc block to add claims, AWS session tags, or replace the
default compound subject for every token minted by imported jobs:
plugins:
- github-actions#latest:
workflow: .github/workflows/deploy.yml
oidc:
claims: [organization_id]
aws-session-tags: [organization_slug, pipeline_id]
subject-claim: pipeline_idThis configuration does not grant OIDC access. Each workflow job must still
declare permissions: {id-token: write} to receive the endpoint.
See Buildkite's AWS setup guide
for the complete IAM configuration and OIDC claims reference
for the full subject format and available claims.
Check syntax, the static job graph, and every declared trigger without contacting Buildkite or executing workflow code:
buildkite-gha validate .github/workflows/ci.ymlThis event-independent result does not claim hosted admission.
To resolve public actions and apply the production upload policy, provide an event snapshot:
buildkite-gha validate \
--profile hosted \
--event-path .buildkite/events/current.json \
.github/workflows/ci.ymlFor a quick push compatibility check, generate a minimal event snapshot:
buildkite-gha validate \
--profile hosted \
--event push \
.github/workflows/ci.yml--event also supports pull_request, merge_group, release, workflow_dispatch, and schedule. Generated release validation uses one stable published snapshot; it is representative static validation, not proof of every release activity. Generated snapshots are not equivalent to real payloads; use --event-path when exact payload data matters.
Use --all-events to evaluate every declared supported event separately:
buildkite-gha validate \
--profile hosted \
--all-events \
.github/workflows/ci.ymlJSON output uses processing-report/v3 to retain each event's result.
An admitted result means the workflow satisfies upload policy. A not-applicable result means the workflow does not declare the selected event and upload would skip it without compiling it. Validation does not execute the workflow or prove that arbitrary action code works without GitHub services. Use --format json for machine-readable output.
See the CLI guide for event snapshots, compilation, direct upload, and agent targeting.
Workflow steps and third-party actions are repository code. Run imported jobs on a disposable, whole-job-isolated queue with no ambient protected credentials. Action containers do not replace that boundary.
See the security model before enabling managed repository access, scoped write tokens, or caching.
Use buildkite-gha help, buildkite-gha help <command>, or buildkite-gha --version for the installed command surface.
MIT. See LICENSE.