Run GitHub Actions workflows in Buildkite
Running GitHub Actions workflows in Buildkite is currently in public preview. To report issues with the preview, open an issue in the buildkite-gha repository. For help migrating to native Buildkite Pipelines steps, contact the Buildkite Support team at support@buildkite.com.
The plugin and runtime are under active development. Review the buildkite-gha v0.8.0 compatibility guide before adding a workflow.
The GitHub Actions Buildkite plugin gives you a quick way to get a supported GitHub Actions workflow running in Buildkite with minimal changes, without first rewriting it as a native Buildkite pipeline. Once the workflow is up and running, you can convert it into native Buildkite Pipelines steps to take full advantage of Buildkite Pipelines features.
During the preview, the quickest way to get started is with a Linux x86-64 workflow in a public github.com repository that doesn't need secrets. Private repository checkout and temporary GitHub tokens are also available in limited cases, but require extra setup, so check what the preview supports and its current limitations before you begin.
Add a GitHub Actions workflow to a pipeline
Create a new pipeline from the template
To create a pipeline for a GitHub Actions workflow:
- From the Buildkite dashboard, select New Pipeline.
- Select the GitHub repository that contains your workflow.
- In the YAML Steps editor, open the Template dropdown and select GitHub Actions.
- In the generated YAML, set
workflowto the path of the workflow file in your repository. - Select Create and run.
Configure the plugin manually
To configure the plugin without using the template, add the following step to your pipeline configuration. Set workflow to the path of the workflow file in your repository. Give the step a unique key so Buildkite can connect it to the jobs created by the plugin:
steps:
- label: ":github: GitHub Actions"
key: "github-actions"
plugins:
- github-actions#v0.7.1:
workflow: ".github/workflows/ci.yml"
version: "0.8.0"
When this step runs, the plugin turns the workflow jobs into a dynamic pipeline. Each generated job depends on the plugin step, so Buildkite waits for the plugin to finish before running them.
For a released runtime, use the following configuration:
| Property | Required | Description | |||
|---|---|---|---|---|---|
| Property | workflow |
Required | Yes | Description | Path to the GitHub Actions workflow in the repository. |
| Property | version |
Required | No | Description | Exact buildkite-gha runtime version to run. When omitted, the plugin uses its default runtime version. |
The v0.7.1 plugin uses runtime 0.7.1 by default. The examples on this page set version to 0.8.0 to use the newer runtime release. If you update the runtime version, use its matching compatibility guide.
Buildkite decides when the pipeline runs, so the workflow's on key doesn't create build triggers. Set up GitHub triggers and schedules in Buildkite, or start a build yourself by selecting New Build or using the REST API.
For manual and scheduled builds, the plugin automatically finds the exact commit after checkout.
The plugin also gives the workflow a GitHub event type based on how the Buildkite build started:
- Pull request builds receive
pull_request. - Branch, tag, scheduled, and manual builds receive
push.
Scheduled and manual builds receive push rather than schedule or workflow_dispatch. The plugin doesn't provide dispatch inputs, so these builds work only with workflows that can run with push event data.
Migrate incrementally
You don't have to convert the whole workflow at once. Imported workflow jobs and native Buildkite Pipelines steps can run in the same build. In this example, the native Deploy step waits for all the imported test jobs to finish:
steps:
- label: ":github: Tests"
key: "github-actions-tests"
plugins:
- github-actions#v0.7.1:
workflow: ".github/workflows/ci.yml"
version: "0.8.0"
- label: "Deploy"
key: "deploy"
depends_on: "github-actions-tests"
command: ".buildkite/deploy.sh"
As you replace jobs with native Buildkite Pipelines steps, the remaining supported workflow jobs can keep running through the plugin. If you want to convert a whole workflow instead, use the Buildkite pipeline converter.
How the plugin and runtime work
The plugin and the buildkite-gha runtime work together to run the workflow. This page calls the keyed command step that runs the plugin the importer step, and the jobs it creates the generated jobs.
Each part has a different job:
-
GitHub Actions Buildkite plugin: Reads your configuration, downloads and verifies the selected
buildkite-gharelease, then starts the upload. -
buildkite-gha: Checks that the workflow is supported, turns its jobs into Buildkite Pipelines command jobs, uploads them, and runs each generated job.
You don't need to install buildkite-gha yourself. The plugin downloads the Linux x86-64 runtime binary and checks its checksum and archive contents before running it.
Jobs that use JavaScript actions need mise 2026.5.12 or later. The runtime checks BUILDKITE_GHA_MISE, then PATH, and downloads and verifies a managed copy if neither provides a compatible version. A runtime image doesn't remove this requirement. Shell-only jobs and jobs that use only native adapters or Docker don't need mise. The importer step and the validate and compile commands don't need it either.
The importer passes the runtime and compiled execution plans to the generated jobs using Buildkite Pipelines artifacts. Each job verifies these files before using them. This process doesn't create a corresponding workflow run in GitHub. Buildkite handles the schedule, logs, retries, cancellations, and build status.
GitHub Actions concepts map to Buildkite Pipelines as follows:
| GitHub Actions concept | Buildkite Pipelines behavior | ||
|---|---|---|---|
| GitHub Actions concept | Workflow run | Buildkite Pipelines behavior | The current Buildkite Pipelines build. |
| GitHub Actions concept | Job | Buildkite Pipelines behavior | A generated Buildkite Pipelines command job. |
| GitHub Actions concept | Static matrix entry | Buildkite Pipelines behavior | A separate generated command job. |
| GitHub Actions concept | needs |
Buildkite Pipelines behavior | Buildkite Pipelines step dependencies. |
| GitHub Actions concept | Steps within a job | Buildkite Pipelines behavior | Steps run together in one compatibility runtime and share a workspace and lifecycle. |
Requirements
Before it can download the runtime and create the workflow jobs, the importer step needs:
- A Linux x86-64 agent.
- Buildkite agent v3.34.1 or later in the v3 release series. Agent v4 isn't supported because the runtime uses the
--reject-secretsoption, which Agent v4 doesn't provide. - Bash,
awk,chmod,cp,curl,dirname,find,grep,gzip,ln,mkdir,mktemp,mv,rm,sed,sha256sum,sort,tar, anduname. - Git when
BUILDKITE_COMMITisn't already a full commit SHA. - Outbound HTTPS access to public GitHub release and action sources.
Generated jobs need a Linux x86-64 execution environment and Buildkite agent v3.130.0 or later. They can run on Buildkite hosted agents, the Agent Stack for Kubernetes, or other self-hosted agents that provide the tools used by the workflow. The runtime tells the agent to skip its usual repository checkout so that it can prepare the workflow's workspace instead.
Every generated-job host needs Bash, buildkite-agent, mktemp, rm, sha256sum, awk, and chmod. Depending on the workflow, it also needs:
-
gitavailable onPATHforactions/checkout. - Docker and Docker Buildx available on
PATHfor Dockerfile actions. The default Buildx builder must use the localdockerdriver. -
tarand either thezstdtool suite orgzipavailable onPATHforactions/cache.
Generated jobs use the pipeline or organization's default agents unless you choose a queue. To send every generated job to a specific queue, set BUILDKITE_GHA_TARGET_QUEUE on the importer step. The runtime sends all accepted Ubuntu runner labels to that queue.
steps:
- label: ":github: GitHub Actions"
key: "github-actions"
env:
BUILDKITE_GHA_TARGET_QUEUE: "gha-preview"
plugins:
- github-actions#v0.7.1:
workflow: ".github/workflows/ci.yml"
version: "0.8.0"
Because the queue can run untrusted workflow code, it must provide whole-job isolation, no ambient protected credentials, and a clean machine for each untrusted job. Persistent self-hosted agents can expose host resources and state left by earlier jobs.
The generated jobs also need network access for anything they download at runtime:
- Jobs that use public GitHub Actions need outbound HTTPS access to
codeload.github.com, where the runtime downloads each action's source archive. - Jobs that use JavaScript actions need outbound HTTPS access to the managed Node.js and
misedownload sources. Actions that declarenode16run on managed Node 16.20.2 and produce a deprecation warning. Actions that declarenode20ornode24run on managed Node 24.18.0. Managed Node binaries require glibc 2.28 or newer. Shell-only workflows don't have this glibc requirement.
When resolving a mutable tag or branch for a public action, the importer uses an available job-scoped GitHub token only for the GitHub API request. If it can't obtain or register the token, it reports a warning and retries anonymously. A lowercase, full 40-character commit SHA doesn't require an API request. The importer and generated jobs download the resolved action archive anonymously from codeload.github.com.
BUILDKITE_GHA_RUNTIME_IMAGE is supported only when generated jobs run on Buildkite hosted agents or Agent Stack for Kubernetes controller v0.30.0 or later. These environments support the generated image step attribute. Set the variable on the importer step to the immutable digest of a toolchain-enabled image that provides /opt/hostedtoolcache. The runtime rejects tags and other mutable image references. Don't set this variable for other self-hosted agent environments. They don't provision the generated job image or /opt/hostedtoolcache, so the job fails before the workflow starts.
Cache the runtime download
On Buildkite hosted agents, attach the plugin cache volume to speed up the importer:
steps:
- label: ":github: GitHub Actions"
key: "github-actions"
cache: "/cache/bkcache/github-actions-buildkite-plugin"
plugins:
- github-actions#v0.7.1:
workflow: ".github/workflows/ci.yml"
version: "0.8.0"
Without this volume, the plugin uses an agent or user cache when one is available, then falls back to a temporary directory. The plugin verifies cached archives before using them. This importer cache is separate from generated-job runtime caching and the workflow's actions/cache behavior.
Supported functionality and limitations
The preview supports an evolving subset of GitHub Actions. The following lists summarize common supported features and limitations:
- Linux x86-64 jobs using
ubuntu-latest,ubuntu-24.04, orubuntu-22.04. These labels identify a compatible runner, but don't give the agent the same tools or image layout as a GitHub-hosted runner. - Bash and
shrun steps. - Static job dependencies and matrices, including
includeandexclude, up to 256 expanded instances per job. - Supported job and step conditions, outputs, and timeouts, plus step-level
continue-on-errorbehavior. - Public JavaScript, composite, local, and compiler-verified Dockerfile actions.
- Local reusable workflows with statically resolvable inputs.
-
actions/checkoutfor the repository and exact commit that triggered the build. Checkout is anonymous for a public repository. For a private repository, it uses Buildkite's repository-provider Git credentials when they are enabled for the job and Buildkite authorizes the repository URL. - Statically resolvable workflow- and job-level
concurrency, mapped to repository-scoped Buildkite Pipelines concurrency groups. - Native-backed
actions/upload-artifactandactions/download-artifact, for the audited action revisions only. -
actions/cachefor the audited revision, using the Buildkite Results service by default. The Buildkite organization must have GitHub Actions cache token minting enabled. Jobs must be able to reach the Results service and the Agent API.
The runtime rejects many unsupported or privileged features before it uploads any jobs. However, some unsupported settings are ignored rather than rejected. Important limitations include:
- GitHub Enterprise Server repositories, non-GitHub repository providers, private actions, and private reusable workflows.
- General workflow secrets, ambient
GITHUB_TOKEN, alternate-repository or alternate-ref checkout, and GitHub-compatible OIDC, includingid-token. - Windows and macOS jobs, and Linux arm64.
- Job and service containers. The runtime can run them, but the current production upload policy doesn't admit them.
-
docker://actions, which the runtime rejects during validation. - Dynamic matrices and remote reusable workflows.
- The matrix
strategy.fail-fastsetting. The runtime accepts this setting but doesn't enforce it, so a failed matrix job won't cancel the others. This differs from the GitHub Actions default. Iffail-fastcontains an expression, the workflow doesn't compile. -
cancel-in-progress. Setting this to a literaltrueat the workflow level produces a warning but doesn't cancel an older build. Job-level settings and expressions don't compile. See Concurrency for more detail. - Unaudited revisions of actions with native support, including checkout, artifacts, and cache.
- The complete
github.eventpayload and GitHub-specific event behavior.
Known preview gaps
You may need to update a workflow before you can run it during the preview:
-
Check
actions/upload-artifactinputs. Thepathinput accepts up to 32 clean, workspace-relative literal paths or final-component*file globs. A leading./is normalized, and a trailing/selects directories only. Recursive globs, exclusions, path expressions, symlinks, and non-regular files aren't supported. The runtime acceptsretention-daysbut treats it as advisory because Buildkite controls artifact retention. Each upload can contain up to 10,000 files and 1 GiB of source or archive data.
See the buildkite-gha v0.8.0 compatibility guide for the supported functionality and limitations of the runtime selected in this page's examples. If a feature isn't listed in the guide, treat it as unsupported.
Treat workflow code as build code
All steps in an imported job share a workspace, environment changes, processes, and action lifecycle. Docker actions provide packaging, not a security boundary. Run imported jobs on a queue that provides whole-job isolation, no ambient protected credentials, and a clean machine for each untrusted job. Review the buildkite-gha security model for the complete trust boundaries.
Concurrency
The runtime turns each static concurrency group into a case-insensitive Buildkite Pipelines concurrency group scoped to the repository. Workflow-level groups use ordered opening and closing gates, while job-level groups use a concurrency limit of one.
Workflow-level groups can use supported github fields and vars. Job-level groups can also use concrete matrix values and inputs from local reusable workflows when their values are known before the job runs. The workflow won't compile if a group can't be resolved, and workflow-level concurrency isn't supported inside a called reusable workflow.
Buildkite queues every waiting entry, unlike GitHub's default behavior of replacing an existing pending entry. If you want similar cancellation behavior, turn on Cancel Intermediate Builds and Skip Intermediate Builds in the pipeline's build settings. These settings work by branch, so they match a workflow concurrency group only when its scope follows the same branch boundaries.
Credentials and tokens
To check out the private repository that triggered the build, enable Buildkite's repository-provider Git credentials for the job. Buildkite must also authorize the repository URL. Without both, checkout is anonymous. This access doesn't provide GITHUB_TOKEN or github.token, and it can't be used for private actions or other repositories.
Buildkite can provide a short-lived token for the repository that triggered the build. The organization must first enable the job-bound token service, and the job must either reference secrets.GITHUB_TOKEN directly or use an action whose default input references github.token.
If the workflow doesn't include a permissions map, the token receives contents: read. A non-empty permissions map replaces that default, while an empty map or a map containing only none doesn't produce a token. The runtime doesn't add the token to the job's initial environment, although an action can make it available to later steps through GITHUB_ENV, as it can on a GitHub runner. General workflow secrets and an ambient GITHUB_TOKEN aren't available.
Protect tokens from untrusted workflow changes
The job-bound token service doesn't decide whether a fork or actor is trusted. If a pull request can change an imported workflow, that workflow can request and use any repository permission enabled by the service. Make sure untrusted workflow changes can't receive write permissions.
Use the buildkite-gha CLI directly
For most workflows, use the plugin. If you need more control or want to diagnose a problem, you can download the buildkite-gha binary from the buildkite-gha releases.
After downloading the release archive, verify it against the published checksums. You can then check a workflow's syntax and static job graph without running it:
buildkite-gha validate .github/workflows/ci.yml
To resolve actions and apply the production upload policy, provide an event snapshot and the production profile:
buildkite-gha validate \
--profile hosted-tokenless \
--event-path event.json \
.github/workflows/ci.yml
The CLI also provides compile and upload commands. The validate and compile commands don't need mise and don't run workflow code. Each command produces a processing report with the status of each validation and generation stage. Use validate --format json for machine-readable output. The compile command writes its report to standard error, while upload writes it to the importer job log. Stages blocked by an earlier failure are reported as not-evaluated, not failed. If a required stage fails, the runtime doesn't publish plans or pipeline output.
Run upload from a keyed Buildkite Pipelines command step so that the BUILDKITE and BUILDKITE_STEP_KEY environment variables are available. The step must use Buildkite agent v3.34.1 or later in the v3 release series; Agent v4 isn't supported.
As with the plugin, generated jobs manage their own mise setup only when their actions need it. They use the pipeline or organization's default agents unless you set BUILDKITE_GHA_TARGET_QUEUE to choose a queue. The plugin handles all of this setup for you, which is why it's the best option for most workflows.
Next steps
- Learn how to migrate from GitHub Actions.
- Translate a GitHub Actions workflow to native Buildkite Pipelines configuration.
- Learn more about using plugins.