In this blog post Secure Ephemeral GitHub Actions Runners on Azure Container Apps we will explain how to stop a shared build server becoming a permanent security risk. If your GitHub workflows run on long-lived virtual machines, those machines may retain files, credentials and unwanted changes between jobs.

The safer model is straightforward. Azure starts a clean runner when GitHub has work waiting, allows it to complete one job, and then destroys it. You avoid paying for idle servers while greatly reducing the opportunity for one compromised workflow to affect the next.

What is an ephemeral GitHub Actions runner?

GitHub Actions automates tasks such as testing software, deploying applications and applying infrastructure changes. A runner is simply the computer that performs those tasks.

GitHub provides hosted runners, but organisations often need self-hosted runners to reach private Azure services, use specialist tools or meet internal security requirements. The problem is that a conventional self-hosted runner usually stays online and is reused.

Need help with Azure AI?

CPI Consulting helps organisations design, build and secure Azure AI solutions, from Azure OpenAI and Microsoft Foundry to agents, governance and production deployment.

Visit CPI Consulting

An ephemeral runner is different. It registers with GitHub for a single job, performs that job and then exits. Azure Container Apps Jobs provides the short-lived computing environment needed to make this practical without operating a Kubernetes cluster or maintaining a fleet of virtual machines.

How the technology works

The design has four main components:

  1. GitHub Actions places a workflow job in a queue.
  2. Azure Container Apps monitors that queue using an event-based scaling rule. Under the hood, it uses KEDA, which is effectively a queue watcher that starts computing resources when work appears.
  3. A Container Apps Job launches a container containing the GitHub runner and your approved build tools.
  4. The runner registers as ephemeral, completes one job and terminates. Azure can then scale back to zero.

This is particularly useful when deployments must access Azure services through a private network. The runner can operate inside an Azure virtual network while still making outbound connections to GitHub.

Why permanent runners create unnecessary risk

A permanent runner is not automatically unsafe, but it creates more work and more ways for things to go wrong. Every runner needs operating system updates, tool updates, security monitoring and regular credential reviews.

More importantly, workflow code runs directly on the runner. A malicious or poorly written workflow may leave behind scripts, altered tools or sensitive files. If the same machine later performs a production deployment, the damage can spread.

Ephemeral runners reduce this persistence risk because each job starts from a known image. They do not eliminate the need to secure workflows, but they remove much of the uncertainty caused by reusing the same machine.

This also supports several goals within Essential 8, the Australian government’s cybersecurity framework that many organisations are expected to follow. In particular, it helps with patching, restricting administrative access and maintaining reliable logs. It does not provide Essential 8 compliance by itself, but it creates a cleaner foundation for demonstrating control.

Build a controlled runner image

The runner image should contain only the tools your workflows actually need. A smaller image is easier to patch, faster to start and less likely to contain forgotten software.

A simple entry script can request a short-lived registration token, configure the runner for one job and start it:

#!/usr/bin/env bash
set -euo pipefail

REGISTRATION_TOKEN=$(curl -fsSL -X POST \
 -H "Authorization: Bearer ${GITHUB_BOOTSTRAP_TOKEN}" \
 -H "Accept: application/vnd.github+json" \
 "${REGISTRATION_TOKEN_API_URL}" | jq -r '.token')

./config.sh \
 --url "${GITHUB_URL}" \
 --token "${REGISTRATION_TOKEN}" \
 --unattended \
 --ephemeral \
 --labels "azure-container-apps,ephemeral"

exec ./run.sh

Never place the bootstrap credential inside the container image. Store it as a protected secret and inject it only when the job starts. Where supported by your design, a GitHub App with narrowly defined permissions and rotating installation tokens is preferable to a broadly permitted personal access token.

Pin the runner image and all important tools to reviewed versions. Rebuild regularly rather than installing the latest packages every time a job starts. The same controlled-container approach applies when your team needs Azure administration tools, as covered in our guide to running Azure PowerShell cmdlets using Docker containers.

Keep identities separate from the runner

The runner registration credential should not also be your Azure deployment credential. These identities serve different purposes and should have different permissions.

For Azure deployments, use OpenID Connect where possible. This allows GitHub and Microsoft Entra ID to exchange short-lived identity tokens without storing a reusable Azure password in GitHub.

permissions:
 contents: read
 id-token: write

jobs:
 deploy:
 runs-on: [self-hosted, linux, x64, azure-container-apps, ephemeral]
 environment: production

 steps:
 - uses: actions/checkout@v7

 - uses: azure/login@v3
 with:
 client-id: ${{ vars.AZURE_CLIENT_ID }}
 tenant-id: ${{ vars.AZURE_TENANT_ID }}
 subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

 - run: az deployment group create --resource-group app-prod --template-file main.bicep

For readability, this example uses major action versions. Production workflows should pin third-party actions to reviewed, full commit identifiers so an unexpected upstream change cannot silently alter your pipeline.

Azure managed identities can also let Container Apps pull the runner image from a private Azure Container Registry without storing a registry password. Keep that image-pull identity limited to read access. Our article on using managed identity securely in production explains the wider password-free identity pattern.

Add network and workflow guardrails

Ephemeral infrastructure is only one security layer. The surrounding controls decide what a runner can reach and which workflows may use it.

  • Use private repositories. Do not allow untrusted public pull requests to execute on a runner connected to internal systems.
  • Create separate runner groups. Development testing and production deployment should not share the same network access or Azure permissions.
  • Set maximum concurrency. A sudden workflow loop should not create hundreds of runners and an unexpected Azure bill.
  • Restrict outbound traffic. Allow access to GitHub, approved package sources and required Azure services rather than the entire internet.
  • Require production approval. Use protected GitHub environments so sensitive deployments need an authorised reviewer.
  • Centralise logs. Send Container Apps execution logs to Log Analytics and alert on failed registrations, unusual scaling and repeated job failures.

These controls turn the runner from a general-purpose server into a narrowly defined business process.

A practical 200-person company scenario

Consider a 200-person software business running three permanent build virtual machines. One can access development systems, while the others can reach test and production. All three remain online, require patching and gradually accumulate tools installed by different teams.

With Container Apps Jobs, the company can create separate ephemeral runner pools for testing and production. Test runners receive no production access. Production runners start only after approval and authenticate to Azure using short-lived tokens.

The business outcome is not simply newer infrastructure. It removes idle servers, reduces patching work, limits the impact of a compromised workflow and produces a clearer audit trail for customers, insurers and compliance reviews.

Know when Container Apps is not the right fit

Container Apps runners are well suited to Linux-based command-line builds, tests and Azure deployments. They are not the best answer for every pipeline.

If a workflow needs Windows, specialist hardware, privileged containers or a local Docker daemon for nested container builds, consider a different design. Azure Container Registry cloud builds, a carefully controlled virtual machine runner or GitHub Actions Runner Controller on Azure Kubernetes Service may be more appropriate.

This decision should be based on workload requirements, not a desire to force every pipeline onto the same platform.

A safer pipeline with less infrastructure to manage

Secure ephemeral GitHub Actions runners combine short-lived computing, controlled container images, password-free Azure authentication and event-based scaling. The result is lower infrastructure cost and a much smaller window for attackers.

CloudProInc brings more than 20 years of enterprise IT experience to these designs. As a Melbourne-based Microsoft Partner and Wiz Security Integrator, we help Australian and international teams connect GitHub, Azure, Defender and Wiz without creating another complex platform they struggle to maintain.

If you are not sure whether your current runners are retaining credentials, costing more than they should or exposing production systems unnecessarily, we are happy to take a practical look with you — no strings attached.