Note
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.
The plugin and runtime are under active development. Review the buildkite-gha v0.26.0 compatibility guide before adding a workflow.
The GitHub Actions Buildkite plugin converts a supported GitHub Actions workflow into native Buildkite Pipelines jobs without creating a GitHub Actions workflow run. This lets you start migrating a workflow before converting it into native Buildkite Pipelines steps.
During the preview, start with a simple workflow in a public github.com repository that targets Linux x86-64. Private event-repository checkout, statically named Buildkite secrets, temporary GitHub tokens, and OIDC are available with additional setup. Check the supported functionality and limitations before you begin.
Add the plugin to a keyed command step in your pipeline configuration. Select the workflow you want to import explicitly:
steps:
- label: ":github: GitHub Actions"
key: "github-actions"
agents:
queue: importer-linux
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.ymlThe selector must be an explicit path to a tracked .yml or .yaml workflow file. When this importer step runs, the plugin uploads one dynamic pipeline containing a Buildkite group for each directly runnable workflow. Each workflow job and static matrix entry becomes a Buildkite Pipelines job that depends on the importer step. The importer step must have a key and must be scheduled explicitly on either a Linux amd64 or native macOS arm64 agent. The plugin's runners mappings schedule generated workflow jobs only; they do not select or change the importer agent.
The Git ref after github-actions# selects the plugin code. Use a specific release such as github-actions#v0.13.0 for an immutable pin, or use github-actions#latest to follow the newest stable plugin release that has passed the required validation. This is separate from the version property below, which selects the buildkite-gha runtime.
Configure runtime selection with the following properties:
| Option | Required | Default | Description |
|---|---|---|---|
workflow |
One of workflow or workflows |
— | One explicit tracked .yml or .yaml workflow path. |
workflows |
One of workflow or workflows |
— | Non-empty array of explicit tracked .yml or .yaml workflow paths. |
version |
No | latest |
Latest stable or an exact buildkite-gha release from 0.9.0 onward. |
source-ref |
No | — | Full buildkite-gha source commit to build for development testing; mutually exclusive with version. |
minimum-release-age |
No | 0s |
Minimum release age used by mise when resolving latest. |
experimental-runner-user |
No | true |
Run generated Linux jobs as a dedicated runner user. Set to false only as a temporary compatibility opt-out. |
oidc |
No | — | Buildkite OIDC token options for jobs that request GitHub-compatible OIDC. Requires a buildkite-gha release with OIDC support. |
runners |
No | — | Fallback runs-on mappings to Buildkite queues and optional immutable Linux image overrides. Agent API runner resolution takes precedence. |
Note
Plugin and runtime versions are independent. Pin version to keep release-version selection stable, or use latest to follow stable runtime releases. Increase minimum-release-age (for example, to 24h) to delay newly published releases. If you update the runtime version, use its matching compatibility guide.
To test unreleased runtime behavior, set source-ref to a full lowercase 40-character commit from the public buildkite/buildkite-gha repository and omit version. The plugin uses mise and Go 1.26.5 to build Linux amd64 and Darwin arm64 executables from that exact source, runs the executable native to the importer agent, and supplies the counterpart to generated jobs. Source commits are for development only and do not use release checksums, attestations, or minimum-release-age.
The plugin schema requires exactly one of workflow or workflows and validates its explicit paths, the runtime-acquisition fields version, source-ref, and minimum-release-age, the boolean experimental-runner-user field, and the admission-level shape of oidc. It passes behavioral configuration through to the selected buildkite-gha runtime, which validates the complete configuration strictly. This allows runtime releases to extend the supported syntax without requiring a companion plugin release.
Use workflow as the simple form for one explicit path:
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.ymlUse the non-empty workflows array when importing multiple explicit paths:
plugins:
- github-actions#latest:
workflows:
- .github/workflows/ci.yml
- .github/workflows/release.ymlConfigure exactly one selector form. Each value must identify one regular, tracked .yml or .yaml file inside the repository. Empty values and arrays, directories, globs, and wildcard selectors are not accepted. Selected paths are canonicalized, sorted, and deduplicated before upload.
Matched workflows are compiled and uploaded in one pipeline transaction. Workflow groups use the workflow's name, falling back to its repository path. Reusable workflows whose only trigger is workflow_call do not create groups, but remain available to matched callers. The upload fails if a selector matches no tracked files or the selection contains no directly runnable workflows. A safely reportable compilation or trigger-translation error in one workflow instead becomes a failing top-level step, allowing other selected workflows to remain in the uploaded pipeline.
Generated Linux job processes run as a dedicated runner user by default. To temporarily retain root execution for compatibility, set experimental-runner-user: false:
steps:
- label: ":github: GitHub Actions"
key: "github-actions"
agents:
queue: hosted
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.yml
version: "0.26.0"
experimental-runner-user: false
runners:
- runs-on: ubuntu-latest
queue: hostedGenerated Linux jobs must initially start as root so the runtime can provision the runner account. That user retains passwordless sudo, so this is not a security boundary. The option does not affect macOS jobs.
Use the oidc block to configure pipeline-owner options for GitHub-compatible OIDC tokens:
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 requires a buildkite-gha release with OIDC support. Releases without that support reject the oidc block during strict behavioral configuration validation, so do not enable it until a supporting runtime release is selected.
The block only affects jobs that already declare permissions: id-token: write; it does not grant OIDC access to other jobs or change the workflow itself. Host JavaScript actions, including those called by composite actions, can request these tokens; shell steps, Docker actions, and actions running in job containers cannot. Identity providers must trust Buildkite's issuer and claims rather than GitHub's. claims adds optional claims to tokens, aws-session-tags duplicates claims into AWS session-tag format, and subject-claim selects one immutable claim as the token subject. The accepted values match buildkite-agent oidc request-token and are validated by buildkite-gha.
The supported top-level triggers select and filter workflow groups as follows:
| GitHub Actions trigger | Buildkite behavior |
|---|---|
push |
GitHub push webhook, including supported branch, tag, and bounded path filters |
pull_request |
GitHub pull_request webhook, including supported base-branch, activity-type, and bounded path filters |
merge_group |
Native Buildkite merge queue build with a verified linked checks_requested webhook |
release |
Native Buildkite release build with a verified linked published, created, or released webhook |
workflow_dispatch |
Buildkite UI or API build |
schedule |
Buildkite scheduled build |
The effective event selects which workflow groups apply. Applicable workflows use only the matching event's condition; triggers for other events are not ORed into that group. Unsupported events alongside supported events are ignored with a warning. Bounded paths and paths-ignore filters are supported for verified linked GitHub branch pushes and pull requests when the local checkout provides complete matching diff evidence. Other unsupported or inexact filters fail the affected workflow rather than broadening when it runs. A workflow_call trigger defines a reusable workflow and does not create a top-level group by itself.
These conditions select groups in a Buildkite build; they do not configure which GitHub webhooks create builds. Configure the corresponding webhook events in the Buildkite pipeline settings. Buildkite also retains ownership of cron schedules: every workflow with on.schedule is eligible during any Buildkite scheduled build.
Imported workflow jobs and native Buildkite Pipelines steps can run in the same build. In this example, the native Deploy step waits for all imported test jobs to finish:
steps:
- label: ":github: Tests"
key: "github-actions-tests"
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.yml
- 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.
The plugin and the buildkite-gha runtime work together to run the workflow:
- The plugin uses an existing compatible
mise, or installs a pinned verified copy, then asks mise to select and run the configuredbuildkite-gharelease or source commit. - The hidden
buildkite-gha plugincommand reads the plugin configuration, checks that the workflow is supported, converts its jobs into Buildkite Pipelines command jobs, uploads them, and runs each generated job.
You do not need to install mise or buildkite-gha. Mise selects, verifies, and caches the release asset matching the importer host: Linux amd64 or Darwin arm64. The importer then verifies and stages the missing same-release counterpart runtime only when a generated job needs it.
Generated jobs that use JavaScript actions also prepare a verified, managed mise installation for the supported Node.js versions. Shell-only generated jobs and jobs that use only native adapters or Docker do not install mise.
The importer passes the runtime and compiled execution plans to generated jobs using Buildkite Pipelines artifacts. Each job verifies these files before using them. Buildkite Pipelines handles scheduling, logs, retries, cancellation, and build status.
The importer step needs:
- A Linux amd64 or Darwin arm64 agent, selected by the importer's own
agentsconfiguration. Generated-jobrunnersmappings do not schedule this step. - Buildkite agent v3.34.1 or later in the v3 release series. Agent v4 is not supported because the runtime uses the
--reject-secretsoption, which Agent v4 does not provide. - Bash,
curl,tar,mktemp,cp, and eithersha256sumon Linux orshasumon macOS, as listed inplugin.yml. The download tools are used only when a compatiblemiseis not already onPATH. - Git when
BUILDKITE_COMMITis not already a full commit SHA. - Outbound HTTPS access to public GitHub release and action sources.
Generated jobs need Buildkite agent v3.130.0 or later and an execution environment matching their runner mapping. Linux x86-64 jobs can run on Buildkite hosted agents, the Agent Stack for Kubernetes, or other self-hosted agents that provide the workflow's tools. Supported macOS labels require a native Darwin arm64 queue. The runtime tells the agent to skip its usual repository checkout so that it can prepare the workflow workspace instead.
Depending on the workflow, generated-job hosts also need:
gitavailable onPATHforactions/checkout.- Docker available on
PATHfor Linux job containers, service containers, and Dockerfile actions. Dockerfile actions also require Docker Buildx, whose default builder must use the localdockerdriver.
Use runners to provide fallback mappings from an exact GitHub runs-on label to a Buildkite queue. During upload, the runtime first asks the job-scoped Buildkite Agent API to resolve each runner selector. An Agent API result takes precedence over both these mappings and the runtime's local presets. Configured ubuntu-latest and ubuntu-24.04 fallback profiles use the Noble hosted-toolchains image by default; ubuntu-22.04 uses Jammy. A Linux mapping may override that default with another digest-pinned image:
steps:
- label: ":github: GitHub Actions"
key: "github-actions"
agents:
queue: importer-macos-arm64
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.yml
runners:
- runs-on: ubuntu-latest
queue: hosted
image: buildkite.namespace-images.com/agent-base@sha256:62a45683afffaae9edfd669c16d2fee23b5a571679f31715e1063dada667ea24
- runs-on: macos-14
queue: macos-sonoma-arm64The top-level agents.queue above schedules the importer on macOS arm64; it is independent of the queues under runners. runs-on is matched after static expressions and matrices are resolved. An explicit image applies only to the matching Linux label, must be an immutable @sha256: reference, and replaces the label's hosted-toolchains default. macOS mappings select a native queue and cannot specify an image. Duplicate labels, unsupported labels, malformed queues or images, and conflicting multi-label targets fail admission. When the Agent API does not return a target, unmapped supported Linux labels retain default Buildkite agent targeting with their immutable hosted-toolchains image, while macos-latest uses the runtime's native macOS fallback. Other unresolved macOS labels fail rather than falling back to Linux.
Warning
Generated jobs may execute untrusted workflow or action code. The selected queue 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.
Generated jobs 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
misedownloads. 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 do not have this glibc requirement.
When resolving a mutable tag or branch for a public action, the importer uses a dedicated action-source token only for public GitHub metadata requests and reuses it across the selected workflows and nested composite actions. If it cannot obtain the token, it reports a warning and retries anonymously. A lowercase, full 40-character commit SHA does not require an API request. Credential-repository metadata requests and all action archive downloads from codeload.github.com remain anonymous.
Configured Linux profiles select an immutable hosted-toolchains image and enable its /opt/hostedtoolcache. An explicit runners[].image override must provide the same tool-cache path and is supported only when the matching jobs run on Buildkite hosted agents or Agent Stack for Kubernetes controller v0.30.0 or later. Do not configure Linux profiles for other self-hosted environments that cannot provision the generated job image. macOS profiles never select an image.
Buildkite Pipelines controls when builds run. Configure branch, tag, schedule, and pull request triggers in Buildkite. The workflows' on keys select groups after a build exists; they do not create Buildkite Pipelines triggers.
For manual and scheduled builds, the plugin finds the exact commit from the checked-out repository when BUILDKITE_COMMIT does not already contain a full commit SHA.
Pull request builds receive pull_request context. Branch and tag builds receive push context. Verified linked merge queue and release webhooks supply merge_group and release context. Buildkite scheduled builds select workflows with a schedule trigger, while manual UI or API builds select workflows with workflow_dispatch; dispatch inputs are not available.
Supported, audited actions/checkout revisions can check out the event repository at its event commit or a static branch. Checkout runs anonymously when repository-provider credentials are not enabled. Private checkout uses Buildkite repository-provider Git credentials when they are enabled and authorized for the job. Direct and recursive GitHub submodules are supported within the compatibility guide's transport and credential boundaries.
Checkout credentials do not populate GITHUB_TOKEN or github.token, enable private actions, or allow alternate repositories, tags, or arbitrary dynamic commits. A workflow can receive a temporary GitHub token only when it makes a supported static token reference and both the Buildkite organization feature and the pipeline's default-off token setting are enabled. When the workflow omits permissions, the runtime requests exactly contents: read without inheriting GitHub repository or organization defaults. Write access requires an explicit top-level permissions map; an empty map or scopes set to none mint no token.
Direct jobs can resolve statically named ${{ secrets.NAME }} references through the destination job's Buildkite secret authority. These are Buildkite secrets, not GitHub repository, environment, event, or fork-scoped secrets. Dynamic secret names and reusable-workflow secret forwarding are unsupported. The compatibility guide describes the complete credential boundary.
Warning
Temporary token issuance verifies the workflow and build provenance. Job-level repository permission maps are accepted but do not alter GITHUB_TOKEN; job-level id-token permissions retain their documented behavior. Jobs expanded from reusable workflows use the top-level requesting workflow's repository permissions because called-workflow permission maps do not narrow GITHUB_TOKEN. Pull request ancestry is capped at contents: read, and merge queue ancestry is denied. Review the workflow-token restrictions before enabling the service.
On Buildkite hosted agents, attach a mise data cache to avoid reinstalling mise and buildkite-gha:
steps:
- label: ":github: GitHub Actions"
key: "github-actions"
cache: "/cache/bkcache/mise"
plugins:
- github-actions#latest:
workflow: .github/workflows/ci.ymlWithout this volume, mise uses the agent or user data directory. Treat the mise data directory as executable state: do not share it with untrusted jobs or principals that can modify it. This importer cache is separate from generated-job runtime caching and the workflow's actions/cache behavior.
The public preview supports an evolving subset of GitHub Actions. Common supported features include:
- Linux x86-64 jobs using
ubuntu-latest,ubuntu-24.04, orubuntu-22.04. These labels identify a compatible runner but do not provide the same tools or image layout as a GitHub-hosted runner. - Native macOS Apple Silicon jobs using
macos-latest,macos-15, ormacos-14when the Agent API, local preset, or a configured fallback resolves the label to a Darwin arm64 queue. - Bash,
sh, andpythonrun steps on Linux and macOS when the selected shell is available onPATH. - Static job dependencies and matrices, including
includeandexclude, up to 256 expanded instances per job. - Supported field-specific expressions in job and step conditions, names, runner selection,
envmaps, defaults, outputs, matrices, concurrency, and reusable-workflow calls. Job timeouts and job-levelcontinue-on-errorremain literal-only. - Supported outputs, timeouts, literal job-level
continue-on-error, and expression-capable step-levelcontinue-on-error. Runtimerunner.tempis available in supported workflow step fields, step conditions, and job outputs, but not job conditions or compile-time positions. - Workspace-confined
hashFiles()in supported step conditions and step runtime fields, with bounded patterns, matches, and input size. - Public JavaScript, composite, local, and compiler-verified Dockerfile actions.
- Local and literal public reusable workflows. Deferred string inputs must be exactly
${{ needs.<job>.outputs.<name> }}and name a direct dependency; compound deferred expressions are unsupported. - Linux job and service containers, including broadly compatible service health checks, credentials, ports, volumes, and the
job.servicescontext. - Statically named Buildkite secrets in direct jobs and opt-in temporary
GITHUB_TOKENand OIDC support within the documented authority boundaries. - Supported, audited revisions of
actions/checkout(including legacy v1.2.0 and v2.8.0 with upgrade warnings),actions/upload-artifact,actions/download-artifact, andactions/cache. See the compatibility guide for exact admitted commits and version-specific behavior.
Important limitations include:
- GitHub repository or environment secrets, ambient
GITHUB_TOKEN, private actions, private reusable workflows, alternate-repository checkout, tags, and arbitrary dynamic checkout commits are not available. - Windows and Linux arm64 jobs are not supported.
- macOS does not provide GitHub-hosted image or Xcode inventory parity. Docker actions, job containers, and service containers are not supported on macOS.
- Dynamic matrices, private reusable workflows, and dynamically selected reusable workflows are not supported.
- GitHub environments, approvals, environment secrets, deployment records, and protection rules are not supported.
- The runtime accepts
strategy.fail-fastbut does not enforce it, so a failed matrix job does not cancel the other matrix jobs. - The complete
github.eventpayload is not available at runtime, although supported immutable event fields can be reduced during compilation. - Unaudited revisions of actions with native support are rejected.
If a feature is not listed in the buildkite-gha v0.26.0 compatibility guide, treat it as unsupported.
Warning
All steps in an imported job share a workspace, environment changes, processes, and action lifecycle. Docker actions provide packaging, not a security boundary. Review the buildkite-gha v0.26.0 security model before running untrusted workflow code.
See the development guide for local tests, CI smoke tests, and release instructions.
This project uses the MIT License. See LICENSE.