Find the Best Cosmetic Hospitals

Explore trusted cosmetic hospitals and make a confident choice for your transformation.

โ€œInvest in yourself โ€” your confidence is always worth it.โ€

Explore Cosmetic Hospitals

Start your journey today โ€” compare options in one place.

What Is GitHub Actions Runner Controller?

GitHub Actions is a CI/CD platform that automates tasks such as building applications, running tests, creating Docker images, and deploying software.

Every GitHub Actions job requires a runner, which is a machine or container responsible for executing the workflow.

GitHub provides two main runner models:

  • GitHub-hosted runners: Virtual machines managed by GitHub.
  • Self-hosted runners: Compute infrastructure operated by your organization.

Actions Runner Controller (ARC) extends the self-hosted approach by running GitHub Actions runners on Kubernetes.

Instead of keeping dedicated virtual machines permanently online, ARC can dynamically create Kubernetes runner pods when workflow jobs arrive and remove them after execution.

Why use ARC on Amazon EKS?

Amazon Elastic Kubernetes Service (EKS) provides a managed Kubernetes control plane. ARC uses Kubernetes resources to orchestrate GitHub Actions runners on the compute resources attached to the EKS cluster.

This combination provides several benefits:

  1. Automatic runner scaling: Runner pods scale with GitHub Actions demand.
  2. Ephemeral execution: Each job can execute in a fresh runner environment.
  3. Infrastructure control: Organizations choose CPU, memory, architecture, images, networking, and security controls.
  4. AWS integration: Workflows can interact with services such as ECR, S3, EKS, and CloudWatch.
  5. Workload isolation: Different runner scale sets can serve different projects or security boundaries.
  6. Cost optimization: Idle runner pods can scale to zero, although the Kubernetes infrastructure and ARC control components still incur costs.

ARC is especially useful for organizations with substantial CI/CD workloads, private infrastructure dependencies, custom build environments, or strict control requirements.

However, it introduces Kubernetes and AWS operations overhead. For small projects using standard build tooling, GitHub-hosted runners may remain simpler and more economical.

2. Understanding the ARC Architecture

The modern GitHub-supported ARC architecture uses Autoscaling Runner Scale Sets.

Its main components are:

ComponentResponsibility
GitHub Actions serviceReceives workflows, schedules jobs, and communicates with runner scale sets
ARC controller managerReconciles Kubernetes resources and manages runner lifecycle
AutoScalingRunnerSetRepresents a logical group of autoscaling GitHub runners
Runner scale-set listenerMaintains a connection with GitHub Actions and receives job availability notifications
EphemeralRunnerSetRepresents the desired group of ephemeral runners
EphemeralRunnerManages the lifecycle of an individual runner
Runner podExecutes a GitHub Actions job
EKS computeProvides the EC2-backed Kubernetes nodes on which runner pods execute
External cacheStores reusable dependencies or build results between ephemeral jobs

Architecture diagram

flowchart TB
    DEV["Developer pushes code"] --> GH["GitHub Actions Service"]

    subgraph EKS["Amazon EKS Cluster"]
        subgraph CONTROL["ARC Control Components"]
            CTRL["ARC Controller Manager"]
            LISTENER["Runner Scale Set Listener"]
        end

        SCALE["AutoScalingRunnerSet"]
        PODS["Ephemeral Runner Pods"]
        NODES["EKS Worker Nodes"]

        LISTENER --> SCALE
        SCALE --> CTRL
        CTRL --> PODS
        PODS --> NODES
    end

    GH <-->|HTTPS job notifications| LISTENER
    GH <-->|Job communication| PODS

    PODS <--> GHC["GitHub Actions Cache"]
    PODS <--> ECR["Amazon ECR Build Cache"]
    PODS <--> S3["Amazon S3 Artifacts"]

    style GH fill:#dbeafe,color:#172554
    style EKS fill:#f8fafc,color:#0f172a
    style CONTROL fill:#d1fae5,color:#064e3bCode language: PHP (php)

The important architectural distinction is that ARC manages runner execution, while GitHub remains responsible for workflow orchestration and job scheduling.

The controller and listener do not execute application build steps. The ephemeral runner pods execute the steps.

Does ARC need an application load balancer?

No. A normal ARC deployment does not require an ALB or a publicly accessible inbound endpoint.

The controller, listener, and runners communicate with GitHub using outbound HTTPS connections.

You need suitable outbound connectivity to GitHub, container registries, package repositories, and any services your workflows depend on.

3. How ARC Executes a GitHub Actions Job

Consider a developer pushing code to a repository configured with:

jobs:
  build:
    runs-on: arc-linux

The execution lifecycle is:

  1. The developer pushes code.
  2. GitHub creates a workflow run and queues the job.
  3. GitHub identifies the compatible runner scale set.
  4. The scale-set listener receives a job availability notification.
  5. ARC adjusts the desired runner capacity.
  6. Kubernetes creates an ephemeral runner pod.
  7. The runner registers with GitHub using just-in-time registration.
  8. GitHub assigns the job to the runner.
  9. The runner restores available caches, executes workflow steps, and publishes results.
  10. After the job finishes, the ephemeral runner terminates and ARC cleans up its Kubernetes resources.

Job execution sequence diagram

sequenceDiagram
    autonumber
    participant Dev as Developer
    participant GH as GitHub Actions
    participant Listener as ARC Listener
    participant ARC as ARC Controller
    participant EKS as Kubernetes
    participant Cache as External Cache

    Dev->>GH: Push commit
    GH->>GH: Queue workflow job
    GH-->>Listener: Job available
    Listener->>ARC: Request runner capacity
    ARC->>EKS: Create ephemeral runner pod
    EKS-->>ARC: Runner pod ready
    EKS->>GH: Register runner
    GH->>EKS: Assign job
    EKS->>Cache: Restore cache
    Cache-->>EKS: Cached dependencies
    EKS->>EKS: Build and test
    EKS->>Cache: Export updated cache
    EKS-->>GH: Report results
    EKS->>GH: Complete runner session
    ARC->>EKS: Clean up runner podCode language: JavaScript (javascript)

This explains why a cache stored only on an ephemeral runner’s local filesystem is not suitable for reuse across multiple jobs.

Once the runner pod disappears, its temporary filesystem normally disappears with it.

External caches solve that problem.

4. ARC Runner Execution Modes

ARC supports different execution configurations. Choosing the right mode is important for security, compatibility, and performance.

ModeHow it worksSuitable workloadsKey consideration
Native runnerWorkflow steps run directly in the runner containerCLI, Python, Node.js, Terraform, testsNo Docker daemon provided automatically
Docker-in-Docker (dind)Docker daemon runs alongside the runnerDocker builds, Docker actions, service containersRequires privileged execution
Kubernetes modeContainer jobs execute in Kubernetes-managed podsContainerized workflowsRequires workspace-sharing configuration
Kubernetes no-volume modeUses container lifecycle hooks for job filesystem transferContainerized workflows without shared PVCsRequires compatibility testing

Recommended approach: Start with native runners for ordinary CLI and dependency-based jobs. Add a separate Docker-capable runner scale set only when the workflow requires it.

Docker-in-Docker is convenient but creates a significant security boundary concern. AWS recommends avoiding privileged Docker-in-Docker workloads on clusters shared with production applications.

Kubernetes mode is not automatically a drop-in replacement for Docker mode. In particular, job container requirements and workspace behavior need to be tested.


Part 2 โ€” Install ARC on Amazon EKS

5. Prerequisites

Before proceeding, you need:

  • An AWS account and an existing Amazon EKS cluster
  • aws CLI with EKS access
  • kubectl
  • Helm 3
  • A GitHub organization or repository where you can configure self-hosted runners
  • Permission to create or install a GitHub App
  • Outbound HTTPS connectivity from Kubernetes pods

The following examples use the Tokyo AWS region, ap-northeast-1. Substitute your own region and cluster name as appropriate.

Step 1: Check installed tools

aws --version
kubectl version --client
helm version

Step 2: Connect kubectl to the EKS cluster

export AWS_REGION="ap-northeast-1"
export EKS_CLUSTER_NAME="arc-lab"

aws eks update-kubeconfig \
  --region "${AWS_REGION}" \
  --name "${EKS_CLUSTER_NAME}"Code language: JavaScript (javascript)

Confirm access:

kubectl cluster-info
kubectl get nodes -o wide
kubectl get namespacesCode language: JavaScript (javascript)

If your cluster has EKS Auto Mode enabled, the managed compute provisioning system can respond to new runner pod requests. Otherwise, configure managed node groups or another supported node autoscaler.

ARC automatically scales runner pods, but it does not independently provision EC2 nodes.

Optional: Create a dedicated EKS test cluster

If you do not already have an EKS cluster, eksctl can create one using this example file.

eks-cluster.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: arc-lab
  region: ap-northeast-1

managedNodeGroups:
  - name: arc-workers
    instanceType: m6i.large
    desiredCapacity: 2
    minSize: 1
    maxSize: 5Code language: HTTP (http)

Create the cluster:

eksctl create cluster -f eks-cluster.yamlCode language: CSS (css)

This creates billable AWS infrastructure. Use it only in an authorized test account and review your expected EKS, EC2, EBS, and networking costs.

For production, a dedicated CI/CD cluster or a strongly isolated runner compute environment is preferable.

6. Configure GitHub Authentication

ARC supports GitHub App authentication and personal access token authentication.

For repository-level and organization-level scale sets, GitHub App authentication is recommended because it avoids tying infrastructure authentication to an individual’s personal access token.

Enterprise-level scale sets have different authentication requirements and currently require a supported classic PAT configuration.

Step 1: Create a GitHub App

In your GitHub organization, open:

Settings โ†’ Developer settings โ†’ GitHub Apps โ†’ New GitHub App

Configure the App using the permissions specified by GitHub’s ARC authentication documentation.

For repository-scoped runner registration:

  • Repository Administration: Read and write
  • Repository Metadata: Read-only
  • Organization Self-hosted runners: Read and write

For organization-scoped runner registration, the repository Administration permission is not required.

Install the App into the relevant organization.

Collect the following:

  • GitHub App ID
  • GitHub App installation ID
  • Downloaded private key in PEM format

Step 2: Create Kubernetes namespaces

Separate the controller from job-executing runner pods.

kubectl create namespace arc-systems
kubectl create namespace arc-runnersCode language: PHP (php)

Step 3: Create the authentication secret

Set the environment variables using the values obtained from the GitHub App.

export GITHUB_APP_ID="YOUR_APP_ID"
export GITHUB_APP_INSTALLATION_ID="YOUR_INSTALLATION_ID"Code language: JavaScript (javascript)

Create the Kubernetes secret:

kubectl create secret generic arc-github-app \
  --namespace arc-runners \
  --from-literal=github_app_id="${GITHUB_APP_ID}" \
  --from-literal=github_app_installation_id="${GITHUB_APP_INSTALLATION_ID}" \
  --from-file=github_app_private_key=./github-app-private-key.pemCode language: JavaScript (javascript)

Verify that the secret exists without printing its contents:

kubectl get secrets -n arc-runnersCode language: JavaScript (javascript)

For production, use Kubernetes secret encryption, strict RBAC, and an approved secret-management integration. Never commit the GitHub App private key to a repository or embed it in Helm values.

7. Install the ARC Controller

GitHub distributes the modern ARC charts using the Open Container Initiative (OCI) format.

The verified ARC chart release at the time of this guide is 0.15.0.

Set the chart version:

export ARC_VERSION="0.15.0"Code language: JavaScript (javascript)

Install the controller:

helm upgrade --install arc \
  --namespace arc-systems \
  --version "${ARC_VERSION}" \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller \
  --waitCode language: JavaScript (javascript)

Verify:

helm list -n arc-systems
kubectl get pods -n arc-systems
kubectl get crds | grep actions.github.comCode language: JavaScript (javascript)

A successful installation should produce an ARC controller manager pod in the Running state.

Do not use legacy instructions that install the old actions-runner-controller chart and RunnerDeployment resources for a new deployment.

8. Create an Autoscaling Runner Scale Set

Create a file named values-arc-linux.yaml.

githubConfigUrl: "https://github.com/YOUR_ORG"

githubConfigSecret: "arc-github-app"

runnerScaleSetName: "arc-linux"

minRunners: 0
maxRunners: 10

template:
  spec:
    containers:
      - name: runner
        image: ghcr.io/actions/actions-runner:latest
        command: ["/home/runner/run.sh"]
        resources:
          requests:
            cpu: "500m"
            memory: "1Gi"
          limits:
            cpu: "2"
            memory: "4Gi"Code language: JavaScript (javascript)

Replace YOUR_ORG with the actual GitHub organization name. For repository-level registration, use the full repository URL instead.

Configuration explained

ParameterExplanation
githubConfigUrlGitHub repository, organization, or enterprise where the scale set belongs
githubConfigSecretKubernetes secret containing ARC GitHub credentials
runnerScaleSetNameLogical runner name used in runs-on
minRunnersMinimum number of available idle runners
maxRunnersMaximum runner capacity
templateKubernetes runner pod definition
resources.requestsCPU and memory requested for Kubernetes scheduling
resources.limitsMaximum CPU and memory allowed for the runner

For this example:

minRunners: 0
maxRunners: 10Code language: HTTP (http)

The runner set can scale down to zero idle runners and scale up to ten runners as demand increases.

Important: Setting both minRunners and maxRunners to zero is different. In current ARC configuration, that combination can be used to drain the queue by preventing new runner pods from being created.

Install the scale set

helm upgrade --install arc-linux \
  --namespace arc-runners \
  --version "${ARC_VERSION}" \
  -f values-arc-linux.yaml \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
  --waitCode language: JavaScript (javascript)

Verify:

helm list -A

kubectl get autoscalingrunnersets -A

kubectl get pods -n arc-runnersCode language: JavaScript (javascript)

The ARC listener should be available even when the configuration has zero idle runner pods.

9. Run the First GitHub Actions Workflow

Inside your GitHub repository, create:

.github/workflows/arc-test.yml

name: ARC EKS Runner Test

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  test:
    runs-on: arc-linux

    steps:
      - name: Runner information
        run: |
          echo "Running on ARC"
          echo "Runner: $RUNNER_NAME"
          echo "Operating system: $RUNNER_OS"
          uname -a
          nproc
          free -h
          df -h

      - name: Validate job execution
        run: |
          echo "ARC job completed successfully"Code language: PHP (php)

Commit the workflow.

Open the repository’s Actions tab, select the workflow, and run it manually.

While the workflow executes, monitor the Kubernetes namespace:

kubectl get pods -n arc-runners -wCode language: JavaScript (javascript)

You should observe a runner pod being created, executing the job, and eventually disappearing after completion.

If the workflow remains queued, inspect the runner scale-set name, listener logs, GitHub App permissions, and Kubernetes capacity.


Part 3 โ€” GitHub Actions Caching Explained

10. Why Caching Is Important for Ephemeral ARC Runners

Consider a Node.js project that installs hundreds of packages.

Without caching, a workflow might repeatedly download many of the same packages, even when its dependency lockfile has not changed.

The same issue affects:

  • npm and pnpm packages
  • Python pip dependencies
  • Maven repositories
  • Gradle dependencies and transforms
  • Go modules
  • Docker image layers
  • Compilers, toolchains, and reusable generated assets

Ephemeral ARC runners generally do not retain local files between separate workflow jobs.

A remote cache allows a new runner to reuse content produced during an earlier execution.

GitHub Actions cache lifecycle

flowchart TD
    START["New ARC runner starts"] --> KEY["Calculate cache key"]
    KEY --> LOOKUP{"Matching cache exists?"}

    LOOKUP -->|Yes| RESTORE["Download and extract cache"]
    LOOKUP -->|No| COLD["Download dependencies normally"]

    RESTORE --> BUILD["Run build and tests"]
    COLD --> BUILD

    BUILD --> RESULT{"Job successful?"}
    RESULT -->|No| END["Finish without normal cache save"]
    RESULT -->|Yes| SAVE{"New cache key and write allowed?"}
    SAVE -->|Yes| UPLOAD["Upload cache archive"]
    SAVE -->|No| END
    UPLOAD --> ENDCode language: JavaScript (javascript)

The first run usually experiences a cold cache. Later runs may restore content through exact or compatible cache-key matches.

GitHub cache entries are immutable. Once a cache exists under a given key and cache version, the normal pattern is to generate a new key when the reusable content changes.

11. GitHub Actions Cache Options Compared

There is no single cache mechanism that is optimal for all workloads.

A useful distinction is between dependency caches, Docker build caches, object storage, and shared filesystems.

Caching solutionBest suited forPersistent across ARC jobs?ComplexityAssessment
actions/cacheGeneral dependencies and reusable directoriesYesLowRecommended general-purpose choice
setup-* action cachingnpm, pip, Maven, Go, and similar toolsYesVery lowBest default for supported package managers
BuildKit type=ghaDocker image build layersYesLowGood for smaller Docker caches
BuildKit type=registry with ECRDocker image build layersYesMediumStrong choice for AWS-centric builds
Amazon S3 with custom scriptsLarge archives and reusable test assetsYesMediumUseful for specialized data
Amazon EFSShared filesystem workloadsYesMediumโ€“HighUse only with carefully designed concurrency
Local disk or emptyDirTemporary workspace within one runner podGenerally noLowFast but not persistent
ElastiCache RedisSmall key/value objects or custom application cache dataYesHighNot a typical CI dependency cache
Amazon RDS PostgreSQLRelational application and test dataYesHighNot recommended as a build-cache backend

A. Native GitHub Actions Cache

This is the simplest general-purpose solution.

Advantages include native GitHub integration, support for self-hosted runners, branch-aware cache access, lockfile-based cache keys, and relatively little operational effort.

Limitations include remote upload/download time, cache eviction, storage quotas, and network transfer through your EKS networking configuration.

B. Docker BuildKit GitHub Cache

The BuildKit gha backend stores Docker build cache data using the GitHub Actions cache service.

It is particularly useful when Docker builds remain within the cache size and usage limits provided by GitHub.

However, Docker still identifies this backend as experimental and documents API throttling considerations.

C. Amazon ECR Build Cache

BuildKit’s registry cache backend can store reusable Docker build layers in ECR.

This is attractive when the workload already builds and pushes images to ECR, requires larger caches, or benefits from AWS-regional storage and controlled access.

It also provides independent lifecycle management, but consumes registry storage and requires suitable IAM permissions.

D. Amazon S3

S3 is appropriate when your workload needs to store reusable archives or datasets that are not naturally handled by the GitHub cache service.

Examples include large test fixtures, downloaded reference data, generated assets, and custom dependency archives.

Implement your own object naming, integrity checking, retention, and concurrency control.

A particularly important distinction: Docker documents its direct BuildKit S3 cache exporter as unreleased. Do not assume that configuring cache-to: type=s3 is a supported production solution in standard Buildx distributions.

E. Amazon EFS

EFS can share a filesystem across multiple pods.

This may be useful for a centrally maintained read-only dependency mirror or specialized workloads that require shared files.

However, sharing mutable build directories between concurrent jobs can create locking problems, corruption risks, and unpredictable performance.

EFS is not automatically faster than downloading a compressed cache archive from GitHub.

F. RDS PostgreSQL

RDS PostgreSQL is designed for relational data.

It does not provide a native backend for actions/cache, nor is it a standard BuildKit cache exporter.

Implementing a file cache using PostgreSQL would require custom application logic, binary storage management, eviction policies, concurrent-access handling, and database administration.

Unless your workflow specifically needs relational data, PostgreSQL introduces unnecessary complexity and expense.

Conclusion: Do not deploy RDS PostgreSQL just to cache ARC build artifacts.


Part 4 โ€” Implement GitHub Actions Caching

12. Node.js and npm Cache

For most Node.js projects, use the built-in cache support in actions/setup-node.

Example:

.github/workflows/node-ci.yml

name: Node.js CI

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  build:
    runs-on: arc-linux

    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: Setup Node.js
        uses: actions/setup-node@v7
        with:
          node-version: "24"
          cache: npm
          cache-dependency-path: package-lock.json

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build application
        run: npm run buildCode language: JavaScript (javascript)

What does this cache?

The setup action caches npm’s package download cache, not the complete node_modules directory.

The workflow still executes npm ci, but npm can reuse previously downloaded package data when available.

This preserves a clean dependency installation while reducing repeated network downloads.

Why use the package-lock.json file?

The lockfile identifies the dependency versions required by the project.

When it changes, the cache key can change, allowing an updated cache to be generated.

For monorepos, use the correct cache-dependency-path to avoid unrelated applications sharing inappropriate cache keys.

13. Python pip Cache

For Python projects, use actions/setup-python with pip caching.

name: Python CI

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  test:
    runs-on: arc-linux

    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-python@v7
        with:
          python-version: "3.13"
          cache: pip
          cache-dependency-path: requirements.txt

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Execute tests
        run: pytestCode language: HTTP (http)

The cache reduces repeated package downloads. It does not guarantee that every dependency installation or wheel build will be eliminated.

14. Maven Cache

For Java applications using Maven:

name: Maven CI

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  build:
    runs-on: arc-linux

    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "21"
          cache: maven

      - name: Maven build
        run: mvn --batch-mode verifyCode language: HTTP (http)

The action manages dependency caching and related Java setup behavior.

For applications with complicated Maven repositories, consider a centralized artifact repository such as Nexus or Artifactory in addition to workflow-level caching.

That is a different architecture from replacing GitHub cache with PostgreSQL.

15. Gradle and Android Cache

Gradle deserves special attention because Android applications often have substantial dependency and build-transform costs.

The official Gradle GitHub Action supports more sophisticated caching than manually archiving arbitrary directories.

Example:

name: Gradle CI

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  build:
    runs-on: arc-linux

    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "21"

      - uses: gradle/actions/setup-gradle@v6
        with:
          cache-provider: basic

      - name: Run Gradle build
        run: ./gradlew build --no-daemonCode language: HTTP (http)

A newer Gradle caching consideration

The official Gradle action now distinguishes between an enhanced caching provider and a basic caching provider.

The enhanced option is the default in its current documentation and uses an additional proprietary caching component. The basic option uses the standard GitHub Actions cache mechanism and is entirely open source.

If you want a straightforward, independently understandable caching configuration, explicitly select:

cache-provider: basicCode language: HTTP (http)

If your organization accepts the enhanced provider’s licensing, data-handling, and service terms, benchmark it separately.

Avoid caching the Gradle User Home simultaneously through setup-gradle, setup-java cache: gradle, and a separate manual actions/cache step. Overlapping mechanisms can interfere with each other.

For Android, also account for Android SDK installation time, toolchain downloads, emulator startup, and build output reuse. An optimized runner image with commonly needed tools preinstalled may help more than dependency caching alone.

16. Custom Cache Keys with actions/cache

When a setup action does not cover your use case, configure actions/cache directly.

- name: Restore custom cache
  id: custom-cache
  uses: actions/cache@v6
  with:
    path: ~/.cache/mytool
    key: ${{ runner.os }}-${{ runner.arch }}-mytool-${{ hashFiles('tools.lock') }}
    restore-keys: |
      ${{ runner.os }}-${{ runner.arch }}-mytool-

- name: Generate reusable data
  if: steps.custom-cache.outputs.cache-hit != 'true'
  run: ./scripts/generate-cache.shCode language: JavaScript (javascript)

Use a stable, specific cache key. Include relevant dependency versions, operating system, architecture, or configuration hashes.

Do not cache secrets, cloud credentials, access tokens, signing keys, or other sensitive data.

For trusted and untrusted workflows, use GitHub’s cache permission model appropriately. Current GitHub Actions supports cache-mode values such as read, write, write-only, and none.


Part 5 โ€” Docker Builds and Amazon ECR Cache

17. Create a Docker-Capable ARC Runner Scale Set

Native ARC runner pods do not automatically provide an operational Docker daemon.

For Docker-dependent builds, create a separate runner scale set with Docker-in-Docker mode.

First create the namespace:

kubectl create namespace arc-dockerCode language: PHP (php)

Create the GitHub App secret again in this namespace because Kubernetes secrets are namespace-scoped.

kubectl create secret generic arc-github-app \
  --namespace arc-docker \
  --from-literal=github_app_id="${GITHUB_APP_ID}" \
  --from-literal=github_app_installation_id="${GITHUB_APP_INSTALLATION_ID}" \
  --from-file=github_app_private_key=./github-app-private-key.pemCode language: JavaScript (javascript)

Create values-arc-docker.yaml:

githubConfigUrl: "https://github.com/YOUR_ORG"

githubConfigSecret: "arc-github-app"

runnerScaleSetName: "arc-docker"

minRunners: 0
maxRunners: 5

containerMode:
  type: "dind"Code language: JavaScript (javascript)

Install it:

helm upgrade --install arc-docker \
  --namespace arc-docker \
  --version "${ARC_VERSION}" \
  -f values-arc-docker.yaml \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
  --waitCode language: JavaScript (javascript)

This mode uses privileged Docker execution. Deploy it only on approved, appropriately isolated compute.

A separate namespace is useful, but namespace separation alone does not provide a sufficient security boundary for privileged workloads. Prefer dedicated CI worker nodes or a dedicated EKS cluster, combined with appropriate network controls.

For organizations prioritizing unprivileged container execution, investigate Kubernetes mode, rootless BuildKit, or a separate managed build service such as AWS CodeBuild.

18. Configure ECR Repositories

The following example assumes your AWS CLI is authenticated to the correct account.

export AWS_REGION="ap-northeast-1"
export APP_REPOSITORY="arc-demo-app"
export CACHE_REPOSITORY="arc-demo-buildcache"Code language: JavaScript (javascript)

Create the repositories if they do not already exist:

aws ecr create-repository \
  --repository-name "${APP_REPOSITORY}" \
  --region "${AWS_REGION}"Code language: JavaScript (javascript)
aws ecr create-repository \
  --repository-name "${CACHE_REPOSITORY}" \
  --region "${AWS_REGION}"Code language: JavaScript (javascript)

The first repository stores final application images.

The second repository stores BuildKit’s reusable build-cache manifests and layers.

Create an AWS IAM role for GitHub Actions with permission to push and pull the required ECR images. Configure a restricted GitHub OIDC trust relationship for the intended repository and branch.

Do not copy an old OIDC trust policy without checking the current sub claim format. GitHub’s OIDC claim format can differ based on repository creation date, organization configuration, and whether an environment is used.

Set these GitHub repository or organization variables:

AWS_ROLE_ARN
ECR_REPOSITORY
ECR_CACHE_REPOSITORY

19. Build Docker Images Using ECR Cache

Create .github/workflows/docker-ecr.yml:

name: Docker Build with ECR Cache

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  id-token: write

jobs:
  docker:
    runs-on: arc-docker

    steps:
      - name: Checkout
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      - name: Login to Amazon ECR
        id: ecr-login
        uses: aws-actions/amazon-ecr-login@v2

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Build and push with registry cache
        uses: docker/build-push-action@v7
        with:
          context: .
          push: true
          tags: |
            ${{ steps.ecr-login.outputs.registry }}/${{ vars.ECR_REPOSITORY }}:${{ github.sha }}
          cache-from: |
            type=registry,ref=${{ steps.ecr-login.outputs.registry }}/${{ vars.ECR_CACHE_REPOSITORY }}:main
          cache-to: |
            type=registry,ref=${{ steps.ecr-login.outputs.registry }}/${{ vars.ECR_CACHE_REPOSITORY }}:main,mode=max,oci-mediatypes=true,image-manifest=trueCode language: JavaScript (javascript)

How this works

The initial Docker build may have no usable external cache.

After a successful build, BuildKit exports reusable layers to the cache repository in ECR.

Subsequent builds can import those layers. Unchanged Dockerfile steps may then avoid expensive rebuilding.

The mode=max setting allows caching of intermediate build layers, which can be particularly valuable for multi-stage Dockerfiles.

The OCI manifest options provide an explicit ECR-compatible cache representation.

A good Dockerfile remains essential. Stable dependency-installation steps should appear before frequently changing application source files so that source changes do not unnecessarily invalidate expensive dependency layers.

Concurrency warning

The example uses the same main cache tag for builds of the default branch. For high-concurrency pipelines, manage cache writers carefully to avoid builds overwriting one another’s registry cache references.

One strategy is to serialize cache exports on the main branch. Another is to use separate branch or version-scoped cache references with controlled lifecycle management.

20. Alternative: Docker BuildKit GitHub Cache

For simpler Docker projects, use BuildKit’s GitHub Actions cache backend.

- name: Setup Docker Buildx
  uses: docker/setup-buildx-action@v4

- name: Docker build with GitHub cache
  uses: docker/build-push-action@v7
  with:
    context: .
    push: false
    cache-from: type=gha,scope=myapp
    cache-to: type=gha,scope=myapp,mode=maxCode language: JavaScript (javascript)

The scope parameter is important when multiple Docker images share one GitHub repository.

Without separate scopes, distinct image builds can overwrite each other’s BuildKit cache entries.

This configuration also requires a compatible Buildx and BuildKit release and suitable GitHub cache write permissions.

For lower-trust workflows that have read-only cache access, use cache-from without attempting an export.

ECR versus GitHub Cache for Docker

FactorGitHub type=ghaECR type=registry
Initial configurationSimplerRequires ECR and IAM
GitHub integrationNative workflow cache serviceAWS registry integration
Larger cachesSubject to GitHub cache quotasSubject to ECR storage and registry limits
Cache visibilityGitHub Actions cache managementECR repositories and image references
Sharing across repositoriesLimited by GitHub cache scopePossible with explicit AWS access
EvictionGitHub cache policiesECR lifecycle policies
AWS VPC connectivityRequires access to GitHub cache servicesCan use ECR and S3 VPC endpoints
Best fitSmaller or moderate Docker build cachesAWS-centric, substantial Docker workloads

Neither backend is universally faster.

GitHub cache may perform very well for small caches with high reuse. ECR may be advantageous when large layers are repeatedly transferred within AWS.

The actual result depends on where your runners execute, layer sizes, hit rates, and data transfer costs.


Part 6 โ€” Performance, Costs and Operations

21. How Much Time Can Caching Actually Save?

Caching is useful when the time spent restoring reusable data is less than the time needed to recreate it.

A practical formula is:

Net time saved = Time to recreate content โˆ’ Cache restore time โˆ’ Additional cache overhead

Consider an illustrative pipeline:

StepWithout cacheWith warm cache
Runner startup and checkout2 minutes2 minutes
Dependency installation4 minutes1 minute
Application build and tests6 minutes6 minutes
Additional cache handling0 minutes30 seconds
Total12 minutes9.5 minutes

This would represent approximately a 21% reduction in overall job duration, even though dependency installation itself becomes substantially faster.

These figures are an arithmetic illustration, not measured ARC benchmarks.

At 1,000 equivalent jobs per month, saving 2.5 minutes per job would avoid about 2,500 runner-minutes, or 41.7 runner-hours.

Whether that translates into lower EC2 cost depends on your node sizes, concurrency, scheduling, idle capacity, and node autoscaling behavior.

For example, if EKS nodes remain running continuously regardless of job duration, faster jobs improve throughput without necessarily reducing the EC2 bill.

When caching can be slower

Caching may make a workflow slower when:

  • Cache archives are larger than the original package downloads.
  • Upload and extraction costs exceed the work avoided.
  • Cache keys change too frequently.
  • Large caches are evicted before reuse.
  • Network throughput is poor.
  • The workload is dominated by compilation or tests rather than reusable downloads.

Measure before assuming.

22. Current GitHub Cache Storage Pricing

GitHub’s current documentation describes the following cache-storage model:

  • Default cache allowance: 10 GB per repository.
  • The configured repository limit can be increased by eligible administrators.
  • Additional cache storage above the included allowance is billed.
  • Published cache-storage price: $0.07 per GB-month.
  • Entries not accessed for over seven days are subject to removal.

GitHub provides these illustrative monthly charges for fully utilized cache capacity:

Total configured and used cacheIllustrative monthly excess-storage charge
10 GB$0
50 GB$2.80
200 GB$13.30
1,000 GB$69.30

Actual billing depends on usage over time and the applicable billing configuration.

These are GitHub cache-storage charges only. They do not include AWS compute, networking, ECR, or other services.

A previously announced per-minute platform fee for self-hosted GitHub Actions was postponed. GitHub’s current billing documentation continues to state that self-hosted runner execution is free of GitHub Actions usage-minute charges. Organizations still pay for the infrastructure hosting their runners.

23. AWS Cost Considerations

For ARC on EKS, consider the following cost categories.

AWS componentCost driverOptimization opportunity
EKSKubernetes cluster control planeAvoid unnecessary test clusters
EC2 / EKS Auto Mode computeRunner and controller node capacityMatch instances to workload and use autoscaling
EBSNode and persistent volume storageAvoid unnecessarily large volumes
NAT GatewayHourly and processed-data chargesAnalyze traffic and use relevant VPC endpoints
Amazon ECRStored images and related usageManage image/cache retention
Amazon S3Object storage and requestsLifecycle rules and appropriate storage classes
CloudWatchLogs, metrics, and retentionUse deliberate log retention policies
EFS, if usedShared filesystem usageAvoid without a measured need
RDS, if unnecessarily addedDatabase compute and storageDo not introduce for normal CI caching

For private-subnet runners, ECR interface endpoints and an S3 gateway endpoint can reduce the need to send eligible AWS service traffic through a NAT Gateway.

GitHub Actions traffic still requires an appropriate path to GitHub’s external services.

VPC endpoints also have their own costs, so evaluate the total rather than assuming endpoints always save money.

24. How to Benchmark Your Cache

A reliable evaluation should test three scenarios:

  1. Cold cache โ€” no reusable cache data.
  2. Warm cache โ€” exact cache hit.
  3. Invalidated cache โ€” dependency or Dockerfile changes invalidate some cached content.

Measure:

  • Overall workflow duration
  • Queue-to-runner-start delay
  • Dependency installation duration
  • Docker build duration
  • Cache restore duration
  • Cache upload duration
  • Cache hit rate
  • Cache archive size
  • Runner CPU and memory consumption
  • EKS compute and networking charges

For a simple shell-command timing example:

- name: Measure dependency installation
  shell: bash
  run: |
    START=$(date +%s)

    npm ci

    END=$(date +%s)
    DURATION=$((END - START))

    echo "npm ci duration: ${DURATION} seconds"
    echo "npm ci duration: ${DURATION} seconds" >> "$GITHUB_STEP_SUMMARY"Code language: JavaScript (javascript)

Collect results over enough executions to account for runner cold starts, varying network conditions, and cache hit/miss patterns.

A comparison based on only one cold build and one warm build is not reliable evidence of sustained performance improvement.

25. Security Best Practices

Self-hosted GitHub runners execute code supplied by workflows. This makes runner security an essential part of the architecture.

Recommended production controls

  1. Prefer ephemeral runners. Avoid reusing contaminated workspaces or lingering credentials.
  2. Isolate CI workloads. Use dedicated runner compute or a dedicated cluster for high-risk builds.
  3. Minimize privileged Docker execution. Do not run privileged Docker builds alongside sensitive production services.
  4. Use short-lived cloud credentials. Prefer GitHub OIDC or appropriately scoped EKS identities.
  5. Apply least privilege. Restrict GitHub Apps, Kubernetes RBAC, and AWS IAM roles.
  6. Protect cache integrity. Restrict cache writes from untrusted workflow events.
  7. Protect secrets. Never include credentials in cache directories or image layers.
  8. Control outbound traffic. Provide only the networking required by builds and package repositories.
  9. Maintain logs and audit trails. Export ephemeral runner logs before pod deletion.
  10. Keep runner images current. GitHub enforces minimum runner compatibility requirements and timely updates.

Important runner-version requirement

GitHub has introduced stronger self-hosted runner version enforcement.

The documented minimum registration version is 2.329.0, and runner versions must remain within the required update window to continue receiving jobs.

For production, pin tested controller and runner versions, automate controlled upgrades, and monitor compatibility requirements.

Do not assume that pinning a runner image indefinitely is safe.

EKS Auto Mode consideration

ARC can benefit from EKS Auto Mode node provisioning, but security and compatibility still matter.

Evaluate privileged Docker workloads separately, particularly if the cluster is also running production services. AWS’s recommendation to avoid Docker-in-Docker on shared application clusters remains relevant.

For more restrictive execution requirements, investigate dedicated nonprivileged build approaches and separate runner node pools.

26. Monitoring and Troubleshooting

Start with these commands:

helm list -ACode language: PHP (php)
kubectl get pods -n arc-systems
kubectl get pods -n arc-runners
kubectl get pods -n arc-dockerCode language: JavaScript (javascript)
kubectl get autoscalingrunnersets -A
kubectl get ephemeralrunners -ACode language: JavaScript (javascript)

To inspect an individual pod:

kubectl describe pod POD_NAME -n arc-runners
kubectl logs POD_NAME -n arc-runners

To inspect controller logs, first identify the actual controller pod or deployment name.

kubectl get deployments -n arc-systems
kubectl logs -n arc-systems deployment/CONTROLLER_DEPLOYMENT_NAMECode language: JavaScript (javascript)

Troubleshooting table

ProblemLikely causeWhat to investigate
Workflow remains queuedIncorrect runs-on, unavailable listener, or runner capacity exhaustedRunner scale-set name, GitHub runner availability, listener logs
Runner pods remain PendingInsufficient resources, scheduling constraints, or node provisioning failurekubectl describe pod, node status, capacity
Listener cannot authenticateInvalid GitHub App permissions, installation ID, or secretAuthentication secret and controller/listener logs
Docker daemon unavailableRunner not configured for Docker mode or Docker sidecar failedcontainerMode, runner pod containers and logs
Privileged pod rejectedAdmission or pod-security policiesNamespace policies and cluster security configuration
GitHub cache missChanged key, branch scope, expiration, or evictionKey calculation, cache listings, workflow logs
Cache export deniedInsufficient cache write permission or low-trust workflow contextcache-mode, trigger type, export settings
ECR push returns AccessDeniedIncorrect IAM permissions or AWS role trustAWS identity, ECR policy, OIDC claims
Docker rebuilds every layerPoor Dockerfile ordering or missing remote cacheDockerfile, cache import/export references
Runner registration rejectedOutdated runner binaryRunner image version and GitHub version enforcement
Kubernetes mode rejects a jobJob container is required by that modecontainer: configuration and hook settings

For GitHub cache management, the GitHub CLI can also help inspect stored entries:

gh cache list --repo YOUR_ORG/YOUR_REPOCode language: PHP (php)

Review retention, cache sizes, branch scope, and current usage before changing limits.

Production upgrade caution

ARC upgrades can involve Kubernetes CRD compatibility changes. Do not assume that upgrading the chart is always equivalent to upgrading an ordinary stateless application.

Review the upgrade procedure for the specific source and target releases, test in staging, preserve configuration, and plan for runner scheduling disruption.

Do not blindly delete ARC CRDs or uninstall production scale sets without confirming their impact.


Part 7 โ€” Recommended Production Architecture

27. The Best Caching Architecture for ARC on EKS

For most organizations running mixed CI/CD workloads, a layered approach is more practical than trying to standardize every build on one cache service.

Recommended design

flowchart TD
    GH["GitHub Actions Workflows"] --> TYPE{"Workflow type?"}

    TYPE -->|Node, Python, Maven| NORMAL["Native ARC Runner"]
    TYPE -->|Gradle or Android| GRADLE["Gradle-Optimized ARC Runner"]
    TYPE -->|Docker images| DOCKER["Isolated Docker Build Runner"]
    TYPE -->|Large test assets| DATA["Data Processing Runner"]

    NORMAL --> GHC["GitHub Dependency Cache"]
    GRADLE --> GC["Gradle Cache + Prepared Toolchain"]
    DOCKER --> ECR["Amazon ECR Registry Cache"]
    DATA --> S3["Amazon S3 Object Storage"]

    GHC --> OUT["Faster Repeated Builds"]
    GC --> OUT
    ECR --> OUT
    S3 --> OUT

    RDS["RDS PostgreSQL"] -.-> NO["Not a general CI cache backend"]

    style GH fill:#dbeafe,color:#172554
    style OUT fill:#d1fae5,color:#064e3b
    style NO fill:#fee2e2,color:#7f1d1dCode language: PHP (php)

A reasonable starting configuration is:

WorkloadARC configurationRecommended cache
Node.js applicationsNative Linux runneractions/setup-node built-in cache
Python applicationsNative Linux runneractions/setup-python built-in cache
Maven applicationsNative Linux runneractions/setup-java Maven cache
Gradle and Android applicationsAppropriately sized or preconfigured runnerGradle official caching, selected provider
Docker buildsIsolated Docker-capable runnerECR registry cache
Lightweight Docker buildsIsolated Docker-capable runnerBuildKit GitHub Actions cache
Large reference/test assetsAppropriate isolated runnerS3 with integrity and lifecycle controls
Integration tests using PostgreSQLRunner with suitable test-service supportDisposable PostgreSQL instance or test service, not a build cache

28. Frequently Asked Questions

Does ARC require PostgreSQL, Redis, or another external database?

No. The normal ARC architecture uses GitHub Actions services and Kubernetes resources. It does not require an external PostgreSQL or Redis database for runner scheduling or registration.

Can standard GitHub Actions Cache be used with EKS runners?

Yes. Ephemeral self-hosted runners can use the GitHub Actions cache service, provided they have compatible runner software, required tools, and network access.

Does setting minRunners to zero make EKS completely free when idle?

No. It eliminates the minimum idle runner capacity, but the controller, listener, EKS control plane, and any remaining worker node resources still consume infrastructure.

Can ECR replace GitHub Actions Cache entirely?

Not directly. ECR registry caching is an excellent solution for Docker build layers, but it is not a native replacement for dependency directories managed by actions/cache.

Is S3 better than GitHub Actions Cache?

Not universally. S3 offers scalable object storage and AWS integration, but implementing dependency caching around it requires additional tooling and maintenance.

Use S3 when data size, reuse patterns, or storage requirements justify the effort.

Is Redis faster than GitHub Actions Cache?

Redis can offer low-latency access for suitable key/value workloads, but CI dependency archives are typically large filesystem objects. Redis is not automatically a better or cheaper solution for them.

Would PostgreSQL cache improve Docker builds?

No native integration provides that capability. Use a BuildKit-compatible external cache, such as the GitHub Actions cache or a container registry, instead.

Should I choose GitHub-hosted runners or ARC?

GitHub-hosted runners generally have lower operational overhead.

ARC becomes more attractive when you need custom compute, private AWS connectivity, specific security boundaries, specialized toolchains, or enough workload volume to justify managing the infrastructure.

Compare total cost and operational responsibility, not only per-minute compute prices.

Conclusion

Actions Runner Controller provides a scalable way to operate GitHub Actions self-hosted runners using Kubernetes and Amazon EKS.

The greatest benefit is not simply running GitHub Actions inside Kubernetes. It is combining ephemeral execution, controlled infrastructure, dynamic runner capacity, and reusable build state.

The most effective caching strategy depends on the workload. Native GitHub Actions caching should be the starting point for dependency-based builds. Docker builds benefit from BuildKit external caching, with ECR being a strong option for AWS-centric systems. S3 and shared storage should be reserved for use cases that justify their added complexity.

PostgreSQL is not a suitable general-purpose replacement for these cache mechanisms.

A production-ready ARC deployment should prioritize isolation, minimum permissions, controlled runner updates, accurate performance measurement, and predictable infrastructure costs.

The goal is not to cache everything. It is to reuse the right work, securely, when the cost of restoring it is lower than the cost of recreating it.


Official Documentation and Research References

The following primary sources were consulted and cross-checked:

  1. GitHub โ€” Actions Runner Controller overview
  2. GitHub โ€” Get started with ARC
  3. GitHub โ€” Deploying runner scale sets
  4. GitHub โ€” Dependency caching reference
  5. GitHub โ€” Actions billing
  6. GitHub โ€” ARC releases
  7. Docker โ€” Cache storage backends
  8. Docker โ€” GitHub Actions cache backend
  9. Docker โ€” Cache management with GitHub Actions
  10. AWS โ€” Amazon ECR VPC endpoints
  11. AWS โ€” Amazon EKS security best practices
  12. GitHub โ€” Self-hosted runner version enforcement

Research verification: October 27, 2026. The configuration examples are reference implementations derived from current documentation, not a claim that they have been executed in a live AWS account. Validate them in staging before production deployment.

Find Trusted Cardiac Hospitals

Compare heart hospitals by city and services โ€” all in one place.

Explore Hospitals
I'm Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms. I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.

Related Posts

List of Industry Standards, Frameworks, Maturity Models and Methodologies

No. Category Standard / Framework One-Line Introduction 1 Software Process Improvement and Testing Maturity Automotive SPICE (ASPICE) An automotive process assessment model used to evaluate and improve…

Read More

Essential Industry Standards, Frameworks, Maturity Models and Methodologies Explained

A Complete Reference Guide to Software Engineering, Automotive SPICE, Cybersecurity, AI Governance, IT Service Management, Business Excellence, Quality Management and Organizational Maturity Introduction Modern organizations rely on…

Read More

Business Excellence and Organizational Maturity: How to Choose the Right Framework

An organization may have a strong strategy and weak execution, disciplined operations and weak workforce development, or reliable project delivery and poor data quality. No single framework…

Read More

An Integrated Organizational Excellence Playbook: From Diagnostic Assessment to Sustainable Improvement

The nine frameworks described in this series address different organizational capabilities. The challenge for leaders is to use them coherently rather than launching nine unrelated maturity programs….

Read More

EFQM Model Explained: A Practical Guide to Organizational Excellence, RADAR Assessment, and Sustainable Performance

A business can meet its quarterly targets and still be heading in the wrong direction. Customer satisfaction might be falling while revenue rises. Employees might deliver exceptional…

Read More

People CMM: A Complete Guide to Workforce Maturity, Competency Development, and Organizational Capability

Organizations often claim that people are their most important asset, yet rely on inconsistent hiring decisions, unclear expectations, informal career development, and managers who have never been…

Read More
Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
0
Would love your thoughts, please comment.x
()
x