iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Runners / Agents

A CI runner is the machine that executes jobs. GitHub Actions, GitLab CI, Buildkite, CircleCI — each provides hosted runners; self-hosted runners give you bigger machines, private network access, or cost control.

Hosted, self-hosted, scaling, security

EXAMPLE
# === GitHub Actions runners ===

# 1) Hosted runners (free for public repos; minutes-based for private)
jobs:
    build:
        runs-on: ubuntu-latest          # Ubuntu 22.04 (or 24.04 with -latest)
        # runs-on: ubuntu-22.04
        # runs-on: macos-latest         # macOS 14 (Apple Silicon)
        # runs-on: macos-13              # Intel
        # runs-on: windows-latest        # Windows Server 2022
        steps:
            - uses: actions/checkout@v4
            - run: npm ci
            - run: npm test

# Specs (GitHub-hosted, free tier):
#   ubuntu-latest:  2 vCPU, 7 GB RAM, 14 GB SSD
#   macos-latest:   3 vCPU, 7 GB RAM, 14 GB SSD
#   windows-latest: 2 vCPU, 7 GB RAM, 14 GB SSD

# Larger runners (paid):
#   runs-on: ubuntu-latest-4-cores
#   runs-on: ubuntu-latest-8-cores
#   runs-on: ubuntu-latest-16-cores-x64
#   GPU runners: T4 / A10G available

# 2) Self-hosted runners
jobs:
    build:
        runs-on: [self-hosted, linux, x64, gpu]
        steps:
            - uses: actions/checkout@v4
            - run: npm ci

# Labels are tags. Match the specific runner by label combinations.

# Install:
# 1. Settings → Actions → Runners → New self-hosted runner
# 2. Run the install commands on the target machine:
#    mkdir actions-runner && cd actions-runner
#    curl -O -L https://github.com/actions/runner/releases/download/v2.X/actions-runner-linux-x64-2.X.tar.gz
#    tar xzf ./actions-runner-linux-x64-2.X.tar.gz
#    ./config.sh --url https://github.com/ORG/REPO --token TOKEN
#    ./run.sh
# 3. Run as a service (Linux):
#    sudo ./svc.sh install
#    sudo ./svc.sh start

# 3) Autoscaling self-hosted with EC2 / Kubernetes
# - actions-runner-controller (Kubernetes operator) — scale on workflow demand
# - Phillips Lang's terraform-aws-github-runner — EC2 autoscaling
# - Hosted services: BuildJet, Runs-on, BlackSmith

# Kubernetes:
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata: { name: builder }
spec:
    replicas: 0                          # scale on demand via autoscaler
    template:
        spec:
            repository: my-org/my-repo
            labels: [self-hosted, linux, x64, builder]

---
apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata: { name: builder-hra }
spec:
    scaleTargetRef:
        name: builder
    minReplicas: 0
    maxReplicas: 20
    metrics:
        - type: PercentageRunnersBusy
          scaleUpThreshold:   '0.75'
          scaleDownThreshold: '0.25'

# 4) Group + targeting
jobs:
    test:
        runs-on: [self-hosted, linux, x64, '${{ matrix.os }}']
        strategy:
            matrix:
                os: [ubuntu-22, ubuntu-24]

# === GitLab Runners ===

# 1) Shared runners (GitLab.com) — free for public projects
build:
    image: node:20
    script:
        - npm ci
        - npm test

# 2) Specific runner via tag
build:
    tags: [docker, large]
    script:
        - npm ci
        - npm test

# 3) Self-hosted Docker executor
# Install GitLab Runner on a VM
sudo apt install gitlab-runner
sudo gitlab-runner register
# Provide GitLab URL + registration token
# Choose 'docker' executor + base image

# /etc/gitlab-runner/config.toml
[[runners]]
  name = "my-runner"
  url  = "https://gitlab.com"
  token = "..."
  executor = "docker"
  [runners.docker]
    image = "alpine:latest"
    privileged = false        # set true only if running docker-in-docker

# 4) Kubernetes executor — autoscaling
[[runners]]
  executor = "kubernetes"
  [runners.kubernetes]
    namespace = "gitlab-runners"
    cpu_request = "500m"
    memory_request = "1Gi"

# === Why self-hosted? ===

# Hosted runner pros:
#   ✅ Zero ops; just write the workflow
#   ✅ Always patched, fresh OS
#   ✅ Pre-installed tools (Node, Python, Docker, AWS CLI, ...)
#   ✅ Pay-per-minute model is straightforward

# Self-hosted runner reasons:
#   ✅ Bigger machines (more CPU/RAM/disk) — needed for big builds
#   ✅ Access to private network (VPC services, internal registries)
#   ✅ GPU access for ML pipelines
#   ✅ Custom hardware (ARM, specific kernels)
#   ✅ Faster repeat builds (warm cache)
#   ✅ Lower cost at scale

# === Self-hosted runner security ===

# CRITICAL: Self-hosted runners run untrusted code from your repo.
# If your repo accepts PRs from forks, runner = pwned.

# Mitigations:
#   ✅ Self-hosted runners ONLY for private repos, OR
#   ✅ Block PR triggers from forks (default in GitHub Actions for self-hosted)
#   ✅ Use ephemeral runners (one job per runner, then destroy)
#   ✅ Isolate networks — runners in their own VPC / namespace
#   ✅ Limit secret access to specific jobs
#   ✅ Audit runner logs + workflow changes
#   ✅ Don't give runners access to long-lived cloud creds — use OIDC
#   ✅ Run runners as low-privilege users + read-only filesystems where possible

# Ephemeral runners (GitHub):
./config.sh --url ... --token ... --ephemeral
# Runs ONE job, then exits. Combine with autoscaling to spin up fresh.

# === Matrix + parallelism ===

jobs:
    test:
        runs-on: ubuntu-latest
        strategy:
            matrix:
                node-version: [18, 20, 22]
                os: [ubuntu-latest, windows-latest, macos-latest]
            fail-fast: false
            max-parallel: 6
        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-node@v4
              with: { node-version: ${{ matrix.node-version }} }
            - run: npm ci
            - run: npm test

# This creates 9 jobs (3 node × 3 os) running in parallel.
# max-parallel caps concurrency to control resource use.

# === Larger / GPU runners ===

jobs:
    train:
        runs-on: ubuntu-latest-4-cores       # 4 vCPU, 16 GB RAM
        steps:
            - run: ./train.py

    inference:
        runs-on:
            - self-hosted
            - gpu                              # tagged runner
        steps:
            - run: ./infer.py

# Alternatives: BuildJet, Runs-on, BlackSmith offer larger runners at lower cost.

# === Cost optimization ===

# 1. Cancel in-progress on PR pushes (saves minutes)
concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}
    cancel-in-progress: true

# 2. Skip duplicate workflows
if: |
    !contains(github.event.head_commit.message, '[skip ci]')

# 3. Reduce log verbosity — fewer log writes = faster
# 4. Use smaller runners when possible
# 5. Cache aggressively (npm, pip, gradle, docker layers)
# 6. Self-host for sustained workloads — cheaper than hosted at scale

# === Monitoring runners ===

# GitHub: Settings → Actions → Runners → see status, last job
# Logs: per-job logs in workflow UI; download as artifact
# Metrics: GitHub Actions API + custom dashboard
# Prometheus: actions-runner-controller exposes metrics endpoint

# === Common bugs ===
#   ❌ Runner offline / not picked up → check connectivity, token, labels
#   ❌ Job hangs forever → set timeout-minutes (default 360 = 6h!)
#   ❌ Self-hosted runner exposes secrets across jobs → use ephemeral
#   ❌ Privileged Docker on shared runner → escape risk
#   ❌ Runner has long-lived AWS keys → use OIDC instead
#   ❌ Forgetting fail-fast: false → one failed matrix cell kills all

# === Best practices ===
#   ✅ Hosted runners for small / open-source projects
#   ✅ Self-hosted with ephemeral mode for private CI at scale
#   ✅ Autoscale on demand (Kubernetes, EC2 autoscaling)
#   ✅ Pin runner versions in critical workflows
#   ✅ Use OIDC instead of long-lived cloud secrets
#   ✅ Set timeout-minutes on every job
#   ✅ Concurrency + cancel-in-progress on PR branches
#   ✅ Matrix builds for cross-platform / cross-version testing
#   ✅ Monitor queue depth + average build time as core CI metrics

Why it matters

GitHub-hosted is the right default; self-hosted (ephemeral, autoscaled, in your VPC) wins when you need bigger machines, private network access, or cost control at scale. Use OIDC instead of long-lived cloud credentials for any runner.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# GitHub: runs-on: ubuntu-latest / windows-latest / macos-latest / self-hosted
# GitLab: tags: [docker]
# Self-hosted runners run on YOUR machine — useful for GPU / restricted networks.
Try it Yourself »

Discussion

Loading…