CodeQL can analyze C# applications on Linux ARM64 runners, which makes ARM-based GitHub Actions infrastructure a practical option for teams that build and test .NET applications on ARM.
The important part is making sure the runner, .NET SDK, CodeQL tooling, dependencies, and build mode all work together. A workflow that works on an x64 runner should not be assumed to behave identically on ARM64.
For C#, CodeQL supports multiple build modes, including none, autobuild, and manual. The none mode can create the CodeQL database without compiling the application, while autobuild and manual involve the project's build process.
Why Run CodeQL on ARM64?
ARM64 runners can make sense when your development and deployment environments already use ARM64.
For example, a team may have:
ARM64-based development machines
ARM64 containers
ARM64 Kubernetes nodes
ARM-based cloud infrastructure
Native ARM64 .NET workloads
Running security analysis on the same architecture can expose build and dependency problems that an x64-only CI pipeline may not catch.
There is also a practical CI benefit. GitHub provides Linux ARM64 hosted runner labels such as:
runs-on: ubuntu-24.04-armARM64 Linux runners are also available as self-hosted runners. GitHub currently lists ARM64 support for Linux, macOS, and Windows self-hosted runners. (GitHub Docs)
How CodeQL Analyzes C#
CodeQL treats C# as a supported compiled language.
At a high level, the process is:
C# source code
|
v
CodeQL database
|
v
CodeQL queries
|
v
Security results
|
v
Code scanning alertsThe database represents the structure of the source code so that CodeQL queries can identify security problems and other defects.
For C#, CodeQL supports:
noneautobuildmanual
The correct choice depends on how the application is built and how much control you need over the analysis.
A Basic ARM64 CodeQL Workflow
A simple GitHub Actions workflow can use an ARM64 Linux runner:
name: CodeQL
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
analyze:
name: Analyze C#
runs-on: ubuntu-24.04-arm
permissions:
security-events: write
packages: read
actions: read
contents: read
strategy:
fail-fast: false
matrix:
language:
- csharp
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v4The exact action versions should be aligned with the versions approved for your organization's workflows.
The important part for ARM64 is the runner selection:
runs-on: ubuntu-24.04-armThat causes the job to execute on a Linux ARM64 environment rather than the normal x64 Linux runner.
Use none Build Mode When Appropriate
One of the useful options for C# is the none build mode.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: csharp
build-mode: noneWith none, CodeQL creates its database directly from the source without requiring a normal application build.
This can simplify the workflow when your goal is source analysis and the repository does not need compilation for the CodeQL database.
It can also reduce the number of architecture-specific build dependencies in the workflow.
However, none does not mean that every project should use it. If your application contains generated code or relies on build steps that affect the source CodeQL sees, a build-based approach may provide more complete analysis.
When to Use Autobuild
autobuild lets CodeQL determine how the C# project should be built.
For a standard .NET project, the workflow can use:
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: csharp
build-mode: autobuild
- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: Analyze
uses: github/codeql-action/analyze@v4For C# on Linux and macOS, CodeQL's build detection can look for a .sln or .csproj and invoke dotnet build. It can also detect build scripts in the repository. (GitHub Docs)
Autobuild is convenient, but it should not be treated as magic. Complex repositories can have multiple solutions, custom build scripts, generated projects, private package feeds, or special build configuration.
In those cases, a manual build is often easier to control.
Using a Manual Build
A manual build gives the workflow explicit control over what CodeQL analyzes.
For example:
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: csharp
build-mode: manual
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Analyze
uses: github/codeql-action/analyze@v4This approach is useful when the repository has a predictable build process.
You can also build a specific solution:
- name: Build application
run: dotnet build src/MyApplication.sln --configuration ReleaseThat prevents unrelated projects in a large repository from becoming part of the analysis.
ARM64 Runner Requirements
The runner architecture is only one part of the setup.
For a C# analysis job, make sure the ARM64 runner has the required .NET SDK and other project dependencies.
A self-hosted runner gives you control over the environment, but that also means you are responsible for maintaining it.
Check the architecture during troubleshooting:
uname -mA Linux ARM64 machine should report:
aarch64Then verify the .NET installation:
dotnet --infoYou should confirm that the SDK version used by the repository is installed and that the expected architecture is being used.
For projects that use global.json, verify that the requested SDK is available on the ARM64 runner.
Private NuGet Packages Need Special Attention
C# applications frequently restore dependencies from private package registries.
A CodeQL workflow can fail before analysis even starts if the ARM64 runner cannot authenticate with the package source.
For example:
- name: Configure NuGet
run: |
dotnet nuget add source \
"$PACKAGE_SOURCE" \
--name private-feed \
--username "$PACKAGE_USER" \
--password "$PACKAGE_TOKEN" \
--store-password-in-clear-textThe credentials should come from GitHub Actions secrets or another approved credential mechanism.
Do not place package credentials directly in the workflow file.
If the project can restore all dependencies from public packages, this problem is simpler. Private feeds need to be available from the ARM64 environment and compatible with the authentication mechanism used by the workflow.
Watch for ARM64-Specific Dependencies
A project may compile successfully on x64 but fail on ARM64 because of a native dependency.
Common examples include:
Native database libraries
Image processing libraries
Cryptography libraries
Native compression libraries
Custom P/Invoke dependencies
Native NuGet packages
Tools distributed only for x64
This is one reason ARM64 CodeQL jobs can be useful even when the primary goal is security scanning.
If the workflow uses autobuild or manual build mode, the application build can expose architecture-specific problems.
For source-only analysis with none, those native dependencies may not be needed for the CodeQL database.
Check the Linux Distribution
CodeQL has an important Linux compatibility requirement.
The CodeQL CLI is currently not compatible with non-glibc Linux distributions such as Alpine Linux, which uses musl by default.
This matters when building custom containers for CodeQL.
For example, a lightweight ARM64 Alpine container may look attractive because of its small size, but it is not a suitable default environment for the CodeQL CLI.
Use a supported glibc-based Linux environment for the analysis job.
Resource Planning
CodeQL analysis can require more resources than a normal compilation.
The recommended resources depend on the size of the codebase. GitHub's current guidance starts at 8 GB RAM and 2 CPU cores for small codebases, with larger projects requiring substantially more memory and CPU. At least 14 GB of disk space is recommended for CodeQL analysis.
For an ARM64 self-hosted runner, do not provision the machine based only on the application's normal build requirements.
A runner that is sufficient for:
dotnet buildmay not provide enough resources for:
dotnet build
+
CodeQL database generation
+
CodeQL query analysisMonitor memory and disk usage when introducing CodeQL to an existing ARM64 CI environment.
Common Problems and Troubleshooting
CodeQL Starts but the Build Fails
First verify the architecture:
uname -mThen check:
dotnet --infoLook for native dependencies that are available on x64 but missing on ARM64.
Package Restore Fails
Check the configured NuGet sources:
dotnet nuget list sourceThen verify that the ARM64 runner can authenticate with private feeds.
CodeQL Cannot Run in the Container
Check whether the container uses glibc.
If the environment is based on Alpine Linux, move the CodeQL execution to a compatible Linux distribution.
Autobuild Selects the Wrong Project
Use manual build mode and explicitly specify the solution or project:
- name: Build
run: dotnet build src/MyApplication.sln --configuration ReleaseThis is usually easier to maintain than trying to make automatic project detection handle a complicated repository.
Analysis Runs Out of Memory
Check the runner's available memory and disk space.
For larger repositories, use a runner with more resources rather than adding random workflow changes. CodeQL analysis is resource-sensitive, and GitHub's recommended hardware scales with codebase size.
none vs autobuild vs manual
Build mode | Best suited for | Main advantage | Main concern |
|---|---|---|---|
| Straightforward source analysis | No application build required | May not fit projects that need build-generated information |
| Standard .NET repositories | Minimal workflow configuration | Less control over complex builds |
| Complex or customized builds | Full control over the build | More workflow maintenance |
There is no universal best mode.
Start with the simplest mode that gives your repository complete enough analysis. Move to a manual build when the repository's build process requires explicit configuration.
Best Practices for ARM64 CodeQL
Keep the CodeQL workflow close to the same build environment used by the application.
Use these practices:
Verify the runner architecture. Confirm that the job is actually running on ARM64.
Pin the .NET SDK when required. If the repository uses
global.json, make sure the requested SDK exists on the ARM64 runner.Choose the build mode deliberately. Do not use
autobuildsimply because it requires less YAML.Test native dependencies. ARM64 can expose packages and tools that do not support the target architecture.
Use supported Linux environments. Avoid building the CodeQL environment around unsupported musl-based distributions.
Provide enough memory and disk. CodeQL resource requirements can be much higher than those of a normal .NET build.
Keep private package access explicit. Make sure the runner can authenticate to every feed required by the project.
Analyze the same projects you build. A successful CodeQL run is more useful when the workflow covers the code that actually ships.
Conclusion
Running CodeQL for C# on Linux ARM64 is mainly an exercise in getting the CI environment right.
The CodeQL analysis itself is not limited to x64. C# is a supported CodeQL language, and GitHub provides Linux ARM64 runners that can be selected directly in GitHub Actions.
The areas that deserve the most attention are the .NET SDK, native dependencies, private package feeds, Linux distribution, CodeQL build mode, and runner resources.
For a simple repository, none or autobuild may be enough. For a larger .NET solution with custom build steps, a manual build gives you much better control over what CodeQL analyzes.
The safest approach is to treat the ARM64 runner as a real build environment, not simply as a different machine type. Once the application can restore and build reliably on ARM64, CodeQL can become another step in that same security pipeline.

Join the conversation! Your thoughts help the community grow.