GitHub Actions supports both x64 and ARM64 runner environments, giving teams more options for building and testing modern applications.
At first glance, moving a workflow from an x64 runner to ARM64 may look as simple as changing:
runs-on: ubuntu-latest
to an ARM64 runner label.
In practice, the processor architecture affects more than the operating system. Native binaries, Docker images, package managers, language runtimes, third-party tools, and compiled dependencies can all behave differently when the architecture changes.
GitHub provides ARM64 hosted runners for supported plans and repositories, including Ubuntu ARM64 environments. GitHub's runner documentation also distinguishes standard x64 and Arm64 runner images and their supported labels.
This article explains what developers should check before moving GitHub Actions workflows to ARM64 runners and how to test the migration safely.
What Is an ARM64 Runner?
ARM64, also called AArch64, is a 64-bit processor architecture based on the ARM instruction set.
Traditional GitHub-hosted Linux CI environments have commonly used x86-64 processors.
The architecture difference can be represented as:
x64
|
+-- Intel/AMD instruction set
|
+-- x86-64 binaries
|
+-- x64 container images
ARM64
|
+-- ARM instruction set
|
+-- ARM64 binaries
|
+-- ARM64 container images
Your application code may not care about this difference.
Native dependencies often do.
For example, a managed .NET application may run on both architectures, while a native library loaded by that application may only have an x64 binary.
That is where migration problems usually appear.
Why Use ARM64 in GitHub Actions?
ARM64 runners can be useful when your production infrastructure or development environments already use ARM-based systems.
Typical examples include:
ARM-based cloud infrastructure
ARM64 container deployments
multi-architecture Docker images
cross-platform application testing
software distributed for both x64 and ARM64
workloads that specifically require ARM64 validation
There is also an important testing benefit.
If your application is eventually deployed to ARM64 infrastructure, testing only on x64 can leave architecture-specific problems undiscovered until later.
Check Runner Availability First
Before modifying a workflow, verify that the ARM64 runner type you want is available for your GitHub plan and repository.
GitHub-hosted runner availability differs by account type and operating system. GitHub also documents separate labels and specifications for Arm64 runners.
Do not assume that every workflow using:
runs-on: ubuntu-latest
can simply be changed to an ARM64 equivalent.
First confirm:
The required ARM64 hosted runner is available.
The repository is eligible to use it.
The required operating system is supported.
Your workflow actions support the architecture.
Your application dependencies support ARM64.
Step 1: Identify Architecture-Specific Dependencies
Start by searching your repository for native dependencies.
Look for:
.dll
.so
.dylib
.exe
and package references that include architecture-specific binaries.
For example, a .NET application may contain:
runtimes/
├── linux-x64/
└── linux-arm64/
This is a good sign because the package explicitly provides architecture-specific assets.
However, if you find only:
runtimes/
└── linux-x64/
you should investigate before switching the CI runner.
A managed application can compile successfully while failing at runtime when it attempts to load an x64 native library on ARM64.
Step 2: Check Your .NET Runtime Configuration
For .NET applications, inspect the runtime identifiers used by the project.
For example:
<PropertyGroup>
<RuntimeIdentifiers>
linux-x64;linux-arm64
</RuntimeIdentifiers>
</PropertyGroup>
This tells the build system that both runtime environments are supported.
If the project only contains:
<PropertyGroup>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>
do not assume ARM64 compatibility.
You may need to update the project configuration and verify every native dependency.
Step 3: Check GitHub Actions Used by the Workflow
Your application is not the only component that needs ARM64 support.
The workflow itself may use third-party actions.
For example:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- uses: third-party/action@v1
The first two actions may support ARM64, but that does not automatically mean the third-party action does.
An action can contain:
JavaScript
Docker
compiled binaries
shell scripts
platform-specific executables
Actions that package native binaries require particular attention.
Before migration, check whether each important action supports ARM64.
JavaScript Actions vs Docker Actions
GitHub Actions can be implemented in different ways.
A JavaScript action may be relatively portable if it relies only on architecture-independent Node.js functionality.
A Docker-based action can be more complicated.
For example:
- uses: vendor/security-scan@v1
The action may internally use:
Docker image
|
+-- x64 binary
If the image does not have an ARM64 variant, the action may fail on an ARM64 runner.
This is why checking the action's implementation and supported platforms is important.
Step 4: Check Docker Images
Docker is one of the biggest areas to validate when moving to ARM64.
Consider:
- name: Build container
run: docker build -t sample-api .
Your Dockerfile may look perfectly normal:
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "SampleApi.dll"]
But the base image must support the architecture on which you are building.
Modern container registries commonly publish multi-platform images.
Conceptually:
sample-api:latest
|
+-- linux/amd64
|
+-- linux/arm64
If the image provides only:
linux/amd64
you cannot assume it will work natively on an ARM64 runner.
Check the Image Architecture
Docker can inspect an image manifest.
For example:
docker buildx imagetools inspect \
mcr.microsoft.com/dotnet/aspnet:10.0
The output can show which platforms are available.
Look for entries such as:
linux/amd64
linux/arm64
If both are present, the image is published for both architectures.
This is especially important for:
base images
database images
testing services
observability tools
security scanners
local development containers
Step 5: Check Multi-Architecture Builds
If your workflow publishes Docker images, ARM64 migration may actually be an opportunity to improve the pipeline.
For example:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
tags: example/app:latest
Here, the final image can target multiple architectures.
However, building a multi-platform image is not the same thing as proving that the application works correctly on both architectures.
You should still test the application on ARM64.
Step 6: Check Package Managers
Language package managers can also expose architecture problems.
For Node.js:
npm install
For Python:
pip install -r requirements.txt
For .NET:
dotnet restore
Most popular packages work across architectures, but native packages can behave differently.
For example, a Node.js package may contain native bindings.
Python packages may depend on native libraries or require compilation.
.NET packages may contain architecture-specific runtime assets.
If installation suddenly attempts to compile something from source, the ARM64 environment may expose a dependency that was hidden on x64.
Step 7: Check Native Build Tools
ARM64 builds may require native compilation.
Check whether your workflow installs tools such as:
gcc
g++
make
cmake
clang
pkg-config
For example:
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
cmake \
pkg-config
Do not automatically add packages just because the runner changed architecture.
First determine what your application actually requires.
The goal is to make the build reproducible rather than depending on the runner's preinstalled environment.
Step 8: Check Shell Scripts
Most shell scripts are architecture-independent.
But some scripts invoke architecture-specific commands.
For example:
./tools/my-x64-tool
will not work natively on ARM64 if the binary is compiled only for x64.
Search for checked-in executables and downloaded command-line tools.
A safer approach is to download the correct binary for the detected architecture.
For example:
ARCH=$(uname -m)
case "$ARCH" in
x86_64)
echo "Using x64 tooling"
;;
aarch64)
echo "Using ARM64 tooling"
;;
*)
echo "Unsupported architecture: $ARCH"
exit 1
;;
esac
This makes architecture assumptions explicit.
Step 9: Check Third-Party CLI Tools
Many CI pipelines download tools during execution.
For example:
- name: Install scanner
run: |
curl -L https://example.com/tool-linux-x64.tar.gz \
-o tool.tar.gz
This is a problem on ARM64 because the URL explicitly requests an x64 binary.
Instead, architecture-aware installation should be used when the vendor provides separate builds.
For example:
ARCH=$(uname -m)
if [ "$ARCH" = "aarch64" ]; then
PACKAGE="tool-linux-arm64.tar.gz"
else
PACKAGE="tool-linux-x64.tar.gz"
fi
The exact implementation depends on the software vendor.
Step 10: Check Test Services
Integration tests often start supporting services.
For example:
services:
postgres:
image: postgres:latest
or:
services:
redis:
image: redis:latest
Before moving the workflow to ARM64, verify that those container images support ARM64.
This is easy to overlook because the application itself may be fully compatible.
A test could fail because the database container cannot start, not because your application has an ARM64 problem.
Testing ARM64 Without Changing Production CI
Do not immediately replace every production workflow.
Instead, create a temporary matrix:
jobs:
test:
strategy:
matrix:
runner:
- ubuntu-24.04
- ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x'
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build --configuration Release
- name: Test
run: dotnet test --configuration Release --no-build
Use the exact ARM64 runner label available for your GitHub environment; GitHub's current runner documentation identifies ARM64 labels separately from x64 labels.
This gives you side-by-side results.
What to Compare
When testing x64 and ARM64, compare more than whether the workflow is green.
Check:
Area | What to Check |
|---|---|
Restore | Dependencies resolve correctly |
Build | Native compilation succeeds |
Tests | Unit tests pass |
Integration tests | Supporting services start |
Containers | Images build correctly |
Packaging | Correct artifacts are produced |
CLI tools | Required binaries execute |
Deployment | Target platform accepts the artifact |
Runtime | Application starts and behaves correctly |
A successful build is only one part of the migration.
Common Problems
Problem 1: Exec Format Error
You may see:
exec format error
This usually indicates that the system attempted to execute a binary built for a different architecture.
Check:
uname -m
and inspect the binary or container image architecture.
Problem 2: Docker Image Does Not Support ARM64
A container may fail to start because the image contains only an x64 variant.
Check the image manifest and replace it with a multi-platform image if appropriate.
Problem 3: Native Package Compilation Fails
A package that previously downloaded an x64 prebuilt binary may try to compile from source on ARM64.
Check the package's ARM64 support and required system dependencies.
Problem 4: Third-Party Action Fails
An action may depend on an x64-only executable.
Check the action's implementation and supported platforms before replacing it.
Problem 5: Tests Pass but Deployment Fails
Your CI artifact may be architecture-specific.
For example:
ARM64 build
|
v
ARM64 binary
|
v
x64 production host
|
v
Failure
Make sure the architecture produced by CI matches the architecture expected by the deployment environment.
ARM64 and x64 Side by Side
Area | x64 | ARM64 |
|---|---|---|
Instruction architecture | x86-64 | AArch64 |
Native x64 binaries | Native | Usually incompatible without translation |
Native ARM64 binaries | Usually incompatible | Native |
Multi-platform containers | Supported | Supported |
Managed application code | Often portable | Often portable |
Native dependencies | Need x64 builds | Need ARM64 builds |
CI testing | Common | Useful for ARM deployments |
The important difference is native compatibility.
A source-code project can be architecture-independent while one dependency is not.
Best Practices
Test Before Switching
Run the workflow on ARM64 before making it the default.
Check Every Native Dependency
Do not stop at the application's main runtime.
Check:
libraries
CLI tools
Docker images
test services
GitHub Actions
build tools
Prefer Multi-Architecture Images
Where possible, use images that publish both:
linux/amd64
linux/arm64
Pin Important Dependencies
Reproducible CI becomes more important when changing architecture.
Use explicit runtime and tool versions where appropriate.
Keep Architecture Visible
If your project supports multiple architectures, document that clearly.
For example:
Supported:
- linux-x64
- linux-arm64
Test the Actual Deployment Artifact
Do not assume that successful compilation means successful execution.
Run the generated artifact in an environment matching the intended production architecture.
Advantages and Disadvantages
Advantages | Disadvantages |
|---|---|
Tests ARM64 production compatibility | Some dependencies may be x64-only |
Useful for multi-architecture applications | Migration requires additional testing |
Helps identify architecture-specific bugs | Third-party actions may have limitations |
Supports ARM-based infrastructure | Native builds may need extra configuration |
Useful for multi-platform container builds | Some tools may need architecture-specific installation |
ARM64 Migration Checklist
[ ] Confirm ARM64 runner availability
[ ] Identify all workflows that need ARM64 testing
[ ] Check GitHub Actions compatibility
[ ] Check native application dependencies
[ ] Check .NET runtime identifiers if applicable
[ ] Check Docker base images
[ ] Check service containers
[ ] Check package-manager dependencies
[ ] Check downloaded CLI tools
[ ] Check shell scripts for architecture assumptions
[ ] Run the complete test suite
[ ] Test container builds
[ ] Test the actual deployment artifact
[ ] Compare x64 and ARM64 results
[ ] Monitor the workflow after migration
Summary
Moving GitHub Actions to ARM64 is more than changing the runner label. The operating system may remain Linux, but the underlying processor architecture changes how native binaries, containers, libraries, package dependencies, and command-line tools behave.
The safest approach is to test ARM64 alongside your existing x64 environment first. Review GitHub Actions, native dependencies, Docker images, test services, downloaded binaries, and deployment artifacts before making ARM64 the default.
For applications that already target ARM-based infrastructure, ARM64 CI testing can also provide an important validation layer. It allows architecture-specific issues to be discovered during development rather than after deployment.
The key question before switching is not simply, "Does my application build on ARM64?"
It is:
"Does every important component of my CI/CD pipeline support ARM64?"
Answering that question before migration will make the transition much more predictable.

Join the conversation! Your thoughts help the community grow.