Skip to content
Latchkey

How to Build a Rust Binary for Multiple Targets in GitHub Actions

Shipping a Rust CLI means producing binaries for platforms you do not run locally; a target matrix builds them all in CI.

Use a matrix over target triples, add each with rustup target add, build with --target, and upload a per-target artifact.

Steps

  • Define a matrix over target triples (and the runner OS for native builds).
  • Add the target with rustup target add.
  • Build with cargo build --release --target.
  • Upload each binary as a distinct artifact.

Workflow

.github/workflows/rust-build.yml
name: Build Rust
on: [push]
jobs:
  build:
    strategy:
      matrix:
        include:
          - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
          - { os: ubuntu-latest, target: aarch64-unknown-linux-gnu }
          - { os: macos-latest, target: aarch64-apple-darwin }
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - run: rustup target add ${{ matrix.target }}
      - run: cargo build --release --target ${{ matrix.target }}
      - uses: actions/upload-artifact@v4
        with:
          name: bin-${{ matrix.target }}
          path: target/${{ matrix.target }}/release/

Notes

  • For Linux cross targets, cross or a linker setup may be needed (e.g. for aarch64).
  • Match the runner OS to the target family for the simplest native builds.

Verify it actually works

A workflow that runs is not a workflow that works. Confirm the behaviour on a real event rather than on a manual dispatch, because trigger conditions, permissions, and context values all differ between the two.

Terminal
# 1. validate the file before pushing
docker run --rm -v "$(pwd):/repo" --workdir /repo rhysd/actionlint:latest -color

# 2. trigger the real event, not workflow_dispatch
git commit --allow-empty -m "ci: verify trigger" && git push

# 3. watch it and read the conclusion, not just the colour
gh run watch
gh run view --log-failed

What usually goes wrong first

  • The workflow file must exist on the default branch before scheduled or dispatch triggers appear at all.
  • GITHUB_TOKEN permissions default to read-only in many organisations. Declare a permissions: block listing every scope the job needs.
  • Fork pull requests get a read-only token and no access to secrets, regardless of workflow configuration.
  • actions/checkout gives you depth 1 on a detached HEAD, so anything needing history or a branch name needs fetch-depth: 0.

Frequently asked questions

How do I build a Rust Binary for Multiple Targets in GitHub Actions?
Use a matrix over target triples, add each with rustup target add, build with --target, and upload a per-target artifact.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card