GitHub Actions — Complete Tutorial & Production Handbook

Audience: Developers, DevOps engineers, platform engineers, SREs, security engineers, technical leads, and architects
Level: Beginner → Intermediate → Advanced → Enterprise
Last verified: 2026-09-26
Primary source: GitHub Actions official documentation, plus the supplied 62-section topic map
Goal: Teach GitHub Actions as a working CI/CD and automation platform—not merely as YAML syntax.


Main Curriculum — Table of Contents

#Chapter
1BASIC — GitHub Actions Foundations
2FUNDAMENTALS — Workflow Syntax and Execution
3FUNDAMENTALS — Events and Workflow Triggers
4FUNDAMENTALS — Jobs
5FUNDAMENTALS — Steps
6ESSENTIALS — Runners
7ESSENTIALS — Variables and Environment Variables
8ESSENTIALS — Contexts
9ESSENTIALS — Expressions
10ESSENTIALS — Secrets
11ESSENTIALS — GITHUB_TOKEN
12ESSENTIALS — Workflow Commands
13ESSENTIALS — Matrix Builds
14ESSENTIALS — Caching
15ESSENTIALS — Artifacts
16ESSENTIALS — Concurrency
17ESSENTIALS — Containers
18ESSENTIALS — Using Actions
19ESSENTIALS — Reusable Automation
20ESSENTIALS — CI Pipelines
21ESSENTIALS — CD Pipelines
22ESSENTIALS — Environments and Deployments
23ADVANCED — Custom Actions
24ADVANCED — Workflow Data Flow
25ADVANCED — Conditional Workflows
26ADVANCED — Error Handling
27ADVANCED — Troubleshooting and Debugging
28ADVANCED — Security Hardening
29ADVANCED — OpenID Connect
30ADVANCED — Artifact Attestations
31ADVANCED — Docker and Container CI/CD
32ADVANCED — Package Publishing
33ADVANCED — Releases
34ADVANCED — Cloud Deployments
35ADVANCED — Kubernetes CI/CD
36ADVANCED — Infrastructure as Code
37ADVANCED — Self-Hosted Runner Architecture
38ADVANCED — Actions Runner Controller (ARC)
39ADVANCED — Workflow Performance
40ADVANCED — Cost Optimization
41ADVANCED — Monorepo Workflows
42ADVANCED — Multi-Repository Workflows
43ADVANCED — Workflow Chaining
44ADVANCED — GitHub API Integration
45ADVANCED — Repository Governance
46ADVANCED — Organization Governance
47ADVANCED — Enterprise Governance
48ADVANCED — Workflow Execution Policies
49ADVANCED — Metrics and Observability
50ADVANCED — Workflow Notifications
51ADVANCED — Workflow Cancellation
52ADVANCED — Pull Request CI Patterns
53ADVANCED — Branch and Release Strategies
54ADVANCED — Deployment Strategies
55ADVANCED — Testing GitHub Actions
56ADVANCED — Workflow Maintenance
57ADVANCED — GitHub Actions Limits
58ADVANCED — GitHub Actions Importer
59ADVANCED — Common CI/CD Design Patterns
60ADVANCED — GitHub Actions Anti-Patterns
61ADVANCED — Production Best Practices
62ADVANCED — Reference and Administration

Hands-on labs

  • Lab A — Production-quality Node.js CI
  • Lab B — Reusable organization CI
  • Lab C — Secure AWS OIDC deployment
  • Lab D — Container-to-Kubernetes deployment
  • Lab E — Terraform plan / approval / apply
  • Lab F — ARC runner fleet for private infrastructure
  • Lab G — Systematic workflow debugging

Suggested learning paths

GoalRecommended chapters
New to GitHub Actions1–12, then Lab A
Application CI/CD engineer1–22, 25–31, 52–56
DevOps / cloud engineer1–22, 28–40, Labs C–F
Platform engineer19, 28–49, 55–62
Security engineer10–11, 14, 18, 28–30, 45–48, 60–61
Enterprise administrator37–40, 45–51, 56–62

How to use this handbook

This handbook is deliberately progressive:

  1. Foundations — learn the execution model, YAML, triggers, jobs, steps, runners, variables, contexts, expressions, secrets, and tokens.
  2. Essentials — build production CI/CD pipelines with matrices, caches, artifacts, environments, reusable workflows, containers, and concurrency.
  3. Advanced engineering — custom actions, security, OIDC, attestations, Docker, Kubernetes, cloud deployments, IaC, ARC, monorepos, cross-repository workflows, APIs, and governance.
  4. Production operations — performance, cost, observability, cancellation, testing, maintenance, limits, migration, anti-patterns, and enterprise administration.
  5. End-to-end labs — reusable examples that can be adapted to real repositories.

Conventions used

MarkerMeaning
Mental modelThe simplest way to think about a concept
Use caseA situation where the feature is useful
Production noteAn operational recommendation
Security noteA security-sensitive behavior
Common mistakeA frequent source of workflow failures
LabA hands-on exercise
2026 noteVersion-sensitive behavior verified against current docs

A note on action versions

Examples use contemporary major versions such as:

- uses: actions/checkout@v6
- uses: actions/setup-node@v7
- uses: actions/cache@v4
- uses: actions/upload-artifact@v4
- uses: actions/download-artifact@v5

For production, security-sensitive third-party actions should be pinned to a reviewed full commit SHA. Major tags are easier to read in teaching examples but are mutable references.


Part I — Foundations

1. BASIC — GitHub Actions Foundations

1.1 What is GitHub Actions?

GitHub Actions is GitHub’s built-in automation platform. It reacts to events in or around a repository and runs one or more automated tasks.

The most familiar use is CI/CD:

  • compile code;
  • run tests;
  • lint and format;
  • build packages or container images;
  • deploy applications;
  • publish releases.

But GitHub Actions is broader than CI/CD. It can also automate repository administration, issue management, security scanning, scheduled maintenance, release notes, infrastructure changes, or API-driven orchestration.

Mental model

Think of GitHub Actions as:

Event -> Workflow -> Jobs -> Steps -> Commands/Actions -> Result

A repository event occurs. GitHub finds matching workflow files. Each workflow creates a workflow run. Jobs are scheduled to runners. Each runner executes steps.

flowchart LR
    A[GitHub or external event] --> B[Workflow trigger]
    B --> C[Workflow run]
    C --> D1[Job: lint]
    C --> D2[Job: test]
    D1 --> E1[Runner]
    D2 --> E2[Runner]
    E1 --> F1[Steps]
    E2 --> F2[Steps]
    F1 --> G[Conclusion]
    F2 --> G

1.2 CI, Continuous Delivery, and Continuous Deployment

These terms are related but not identical.

PracticeMain questionTypical GitHub Actions behavior
Continuous Integration“Is this change safe to merge?”Build, lint, unit test, integration test
Continuous Delivery“Is a release ready to deploy?”Build, package, publish, stage, wait for approval
Continuous Deployment“Can every validated change automatically reach production?”CI + automatic production deployment
Repository automation“Can repetitive GitHub work be automated?”Labels, issues, PR comments, releases, housekeeping
DevOps automation“Can code and operations be connected?”IaC, cloud deployment, security scans, notifications

Example: smallest useful CI workflow

Create:

.github/workflows/ci.yml
name: CI

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Run a command
        run: echo "CI is running"

That file contains the core of GitHub Actions:

  • on — when to run;
  • jobs — what units of work exist;
  • runs-on — where a job runs;
  • steps — ordered tasks inside a job;
  • uses — invoke an action;
  • run — execute a shell command.

1.3 Core terminology

TermMeaning
WorkflowYAML-defined automation stored in .github/workflows
Workflow runOne execution instance of a workflow
EventActivity that can trigger a workflow
TriggerWorkflow configuration matching an event
JobIndependently scheduled unit of work
StepOrdered task inside a job
ActionReusable executable automation component
RunnerMachine executing a job
CommandShell statement executed by a run step
ArtifactFile retained from a workflow
CacheReusable dependency/build data intended to speed later runs
ContextStructured runtime information, such as github or matrix
Expression${{ ... }} logic evaluated by GitHub
SecretProtected sensitive value
VariableNon-sensitive configuration value
EnvironmentNamed deployment target with variables, secrets, and protections

1.4 Architecture

GitHub Actions has two broad planes:

  1. Control plane — GitHub receives events, evaluates workflows, creates runs, schedules jobs, manages permissions, logs, artifacts, caches, and API objects.
  2. Execution plane — runners execute jobs.
flowchart TB
    subgraph GitHub["GitHub control plane"]
        R[Repository]
        E[Event]
        W[Workflow parser]
        S[Scheduler]
        T[Token / permissions]
        L[Logs, cache, artifacts]
        R --> E --> W --> S
        T --> S
        S --> L
    end

    subgraph Exec["Execution plane"]
        H1[GitHub-hosted runner]
        H2[Larger runner]
        H3[Self-hosted runner]
        H4[ARC ephemeral runner pod]
    end

    S --> H1
    S --> H2
    S --> H3
    S --> H4
    H1 --> L
    H2 --> L
    H3 --> L
    H4 --> L

1.5 Workflow lifecycle

A simplified lifecycle is:

flowchart TD
    A[Event occurs] --> B{Workflow trigger matches?}
    B -- No --> Z[Nothing runs]
    B -- Yes --> C[Create workflow run]
    C --> D[Evaluate workflow-level expressions]
    D --> E[Create eligible jobs]
    E --> F{Job dependencies satisfied?}
    F -- No --> G[Wait / skip]
    F -- Yes --> H[Queue job]
    H --> I[Assign compatible runner]
    I --> J[Prepare job]
    J --> K[Execute steps]
    K --> L[Post-job cleanup]
    L --> M[Upload logs/results]
    M --> N[Calculate job conclusion]
    N --> O[Calculate workflow conclusion]

A workflow can be:

  • queued;
  • in progress;
  • waiting for an environment approval;
  • completed;
  • cancelled.

Jobs and steps have their own statuses and conclusions.

1.6 Workflow files

Workflow files must be placed under:

.github/workflows/

Both extensions are valid:

ci.yml
release.yaml

A repository can contain many workflows:

.github/
└── workflows/
    ├── ci.yml
    ├── security.yml
    ├── release.yml
    ├── deploy-staging.yml
    └── deploy-production.yml

Naming recommendation

Use a filename for humans and a name for the UI:

name: Pull Request CI

Good filenames:

pull-request-ci.yml
release.yml
deploy-production.yml
terraform-plan.yml

Avoid:

workflow1.yml
new.yml
test2.yml

1.7 Basic workflow structure

name: Application CI

run-name: >-
  CI for ${{ github.ref_name }} by @${{ github.actor }}

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

env:
  NODE_ENV: test

defaults:
  run:
    shell: bash

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Setup Node
        uses: actions/setup-node@v7
        with:
          node-version: "24"

      - name: Install
        run: npm ci

      - name: Test
        run: npm test

Key hierarchy

Workflow
├── metadata/configuration
│   ├── name
│   ├── run-name
│   ├── on
│   ├── permissions
│   ├── env
│   ├── defaults
│   └── concurrency
└── jobs
    └── job_id
        ├── runs-on
        ├── permissions
        ├── needs
        ├── if
        ├── environment
        ├── strategy
        └── steps
            ├── uses
            └── run

2. FUNDAMENTALS — Workflow Syntax and Execution

2.1 YAML fundamentals for GitHub Actions

YAML is whitespace-sensitive. Indentation represents structure.

Mappings

name: CI

name is a key and CI is its value.

Nested mappings

permissions:
  contents: read

Sequences

branches:
  - main
  - release/**

Short form:

branches: [main, release/**]

Multiline strings

Literal style preserves line breaks:

run: |
  echo "first"
  echo "second"

Folded style joins lines where possible:

run-name: >-
  Deploy ${{ github.ref_name }}
  by ${{ github.actor }}

Strings and quoting

When ambiguity is possible, quote values:

node-version: "24"

Quoting is especially useful for:

  • version numbers;
  • strings containing :, #, {, };
  • values that YAML might interpret as another type.

Comments

# Run only on main
on:
  push:
    branches: [main]

Common YAML mistake: indentation

Wrong:

jobs:
build:
  runs-on: ubuntu-latest

Correct:

jobs:
  build:
    runs-on: ubuntu-latest

2.2 YAML anchors and aliases

Modern GitHub Actions supports YAML anchors for reuse.

env: &common-env
  NODE_ENV: test
  CI: "true"

jobs:
  unit:
    runs-on: ubuntu-latest
    env: *common-env
    steps:
      - run: npm test

  integration:
    runs-on: ubuntu-latest
    env: *common-env
    steps:
      - run: npm run test:integration

Use anchors for small static YAML reuse. Use reusable workflows or composite actions for meaningful automation reuse.

Reuse toolBest for
YAML anchorRepeating small mappings/lists
Composite actionRepeating a sequence of steps
Reusable workflowRepeating one or more complete jobs
Workflow templateStandard starting point for many repositories

2.3 Workflow-level syntax

Important top-level keys:

KeyPurpose
nameWorkflow display name
run-nameDynamic run display name
onTriggers
permissionsDefault GITHUB_TOKEN permissions
envWorkflow-wide environment variables
defaultsDefault shell or working directory
concurrencySerialize/cancel related runs
cache-modeControl workflow cache read/write access
jobsJob definitions

defaults.run

defaults:
  run:
    shell: bash
    working-directory: ./app

This applies to run steps unless overridden.

2.4 Job-level syntax

A job is identified by its map key:

jobs:
  unit_tests:
    name: Unit Tests
    runs-on: ubuntu-latest

Useful job-level keys:

jobs:
  deploy:
    name: Deploy
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest

    permissions:
      contents: read
      id-token: write

    environment:
      name: production
      url: https://example.com

    timeout-minutes: 30

    concurrency:
      group: production
      queue: max

    env:
      APP_NAME: demo

    steps:
      - run: ./deploy.sh

2.5 Step-level syntax

A step either invokes an action or runs a command.

Action step:

- name: Checkout
  uses: actions/checkout@v6

Shell step:

- name: Test
  run: npm test

Configured action:

- name: Setup Node
  uses: actions/setup-node@v7
  with:
    node-version: "24"
    cache: npm

Conditional step:

- name: Publish
  if: github.ref == 'refs/heads/main'
  run: npm publish

Step-specific environment:

- name: Integration tests
  env:
    API_URL: https://staging.example.com
  run: npm run test:integration

2.6 Execution boundaries matter

Each job normally gets its own runner. Therefore:

  • files are not automatically shared between jobs;
  • environment variables are not automatically shared between jobs;
  • processes are not shared between jobs;
  • use outputs, artifacts, external stores, or reusable workflow outputs to pass data.

Inside a job, steps share the same filesystem, but each run step starts its own process.

This does not persist:

- run: export VERSION=1.2.3
- run: echo "$VERSION"   # empty

This does persist to later steps:

- run: echo "VERSION=1.2.3" >> "$GITHUB_ENV"
- run: echo "$VERSION"

2.7 2026 syntax: background and parallel steps

GitHub Actions now supports asynchronous/background steps and parallel step groups.

Background step

steps:
  - name: Start server
    id: server
    run: npm start
    background: true

  - name: Run tests
    run: npm test

  - name: Stop server
    cancel: server

Parallel group

steps:
  - uses: actions/checkout@v6

  - parallel:
      - name: Build frontend
        run: npm run build:frontend

      - name: Build backend
        run: npm run build:backend

      - name: Build docs
        run: npm run build:docs

  - name: Test combined result
    run: npm test

Use job-level parallelism for strong isolation and independent runners. Use step-level parallelism only when the tasks belong in the same job and can safely share the same runner.


3. FUNDAMENTALS — Events and Workflow Triggers

3.1 Trigger model

on tells GitHub when a workflow is eligible to run.

on: push

Multiple triggers:

on:
  push:
  pull_request:
  workflow_dispatch:

Filtered trigger:

on:
  push:
    branches:
      - main
      - "release/**"
    paths:
      - "src/**"
      - "!src/docs/**"

3.2 Event categories

CategoryExamplesUse cases
Code activitypush, pull_requestCI
Review activitypull_request_review, issue_commentapproval/automation
Release activityrelease, registry_packagepublishing
Repository lifecyclecreate, delete, fork, publicgovernance
Deploymentdeployment, deployment_statusCD integration
Manualworkflow_dispatchoperator-triggered jobs
Scheduleschedulenightly scans, maintenance
Externalrepository_dispatchAPI-driven automation
Reuseworkflow_callreusable workflows
Chainingworkflow_runprivileged follow-up workflows
Merge queuemerge_grouprequired checks for merge queues

3.3 Important events

push

on:
  push:
    branches: [main]
    tags:
      - "v*"

Use for:

  • branch CI;
  • main-branch builds;
  • tag-based releases.

pull_request

on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]
    branches: [main]

Use for untrusted PR validation. Fork PRs receive restricted tokens and normally do not receive repository secrets.

pull_request_target

This event runs in the security context of the base repository.

Use it for metadata operations such as:

  • adding labels;
  • commenting;
  • assigning reviewers;
  • triage.

Do not use it to execute untrusted PR code with privileged credentials.

workflow_dispatch

on:
  workflow_dispatch:
    inputs:
      environment:
        description: Deployment environment
        type: environment
        required: true

      version:
        description: Version to deploy
        type: string
        required: true

      dry_run:
        description: Dry run
        type: boolean
        default: true

Reference inputs:

${{ inputs.environment }}
${{ inputs.version }}
${{ inputs.dry_run }}

schedule

on:
  schedule:
    - cron: "15 2 * * 1-5"

Scheduled workflows run from the default branch. Treat cron as UTC unless the current GitHub feature/documentation for your account explicitly supports otherwise.

Common cron examples:

ScheduleCron
Every hour0 * * * *
Daily 02:000 2 * * *
Weekdays 09:000 9 * * 1-5
Sundays midnight0 0 * * 0

repository_dispatch

Use when another system should trigger a repository workflow.

on:
  repository_dispatch:
    types: [deploy-request]

Payload:

{
  "event_type": "deploy-request",
  "client_payload": {
    "environment": "staging",
    "version": "1.4.7"
  }
}

Use:

${{ github.event.client_payload.environment }}

workflow_run

Useful for privilege separation:

on:
  workflow_run:
    workflows: ["PR CI"]
    types: [completed]

A common architecture is:

flowchart LR
    A[Untrusted PR] --> B[Low-privilege CI workflow]
    B --> C[Tests + artifact]
    C --> D[workflow_run]
    D --> E[Privileged follow-up workflow]
    E --> F[Comment / publish / deploy]

The downstream workflow must still treat artifacts and data produced by the untrusted upstream workflow as untrusted input.

3.4 Filters

Branches

on:
  push:
    branches:
      - main
      - "release/**"

Branch ignore

on:
  push:
    branches-ignore:
      - "docs/**"

Tags

on:
  push:
    tags:
      - "v[0-9]+.[0-9]+.[0-9]+"

Paths

on:
  pull_request:
    paths:
      - "backend/**"
      - ".github/workflows/backend-ci.yml"

Negative patterns

paths:
  - "src/**"
  - "!src/docs/**"

Order matters when positive and negative patterns are combined.

3.5 Event payloads

Useful references:

- name: Show event
  run: |
    echo "name=$GITHUB_EVENT_NAME"
    cat "$GITHUB_EVENT_PATH"

Expression equivalent:

${{ github.event }}

Do not dump entire contexts into production logs without considering sensitive values.


4. FUNDAMENTALS — Jobs

4.1 Job architecture

Jobs run in parallel by default.

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: echo lint

  test:
    runs-on: ubuntu-latest
    steps:
      - run: echo test
flowchart LR
    W[Workflow] --> L[Lint job]
    W --> T[Test job]
    W --> S[Security job]

4.2 Sequential jobs with needs

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo build

  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - run: echo deploy
flowchart LR
    B[Build] --> T[Test] --> D[Deploy]

4.3 Fan-out and fan-in

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo build

  unit:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo unit

  integration:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo integration

  security:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo security

  deploy:
    needs: [unit, integration, security]
    runs-on: ubuntu-latest
    steps:
      - run: echo deploy
flowchart LR
    B[Build] --> U[Unit]
    B --> I[Integration]
    B --> S[Security]
    U --> D[Deploy]
    I --> D
    S --> D

4.4 Job conditions

if: github.ref == 'refs/heads/main'

Status functions:

if: success()
if: failure()
if: cancelled()
if: always()

Cleanup pattern:

cleanup:
  if: always()
  needs: [test]
  runs-on: ubuntu-latest
  steps:
    - run: ./cleanup.sh

Prefer:

if: ${{ !cancelled() }}

when you want downstream processing after success/failure but do not want expensive work after explicit cancellation.

4.5 Job outputs

Step → job → dependent job:

jobs:
  version:
    runs-on: ubuntu-latest

    outputs:
      release_version: ${{ steps.v.outputs.value }}

    steps:
      - id: v
        run: echo "value=1.8.0" >> "$GITHUB_OUTPUT"

  deploy:
    needs: version
    runs-on: ubuntu-latest

    steps:
      - run: echo "Deploying ${{ needs.version.outputs.release_version }}"

Flow:

flowchart LR
    S[Step output] --> J[Job output]
    J --> N[needs context]
    N --> D[Dependent job]

4.6 Job conclusions

Typical conclusions include:

  • success;
  • failure;
  • cancelled;
  • skipped.

A downstream job normally skips if a required dependency fails unless its if expression says otherwise.


5. FUNDAMENTALS — Steps

5.1 Step types

Action step

- uses: actions/checkout@v6

Command step

- run: npm test

5.2 Step IDs and outputs

- name: Calculate version
  id: version
  run: echo "value=2.1.0" >> "$GITHUB_OUTPUT"

- name: Use version
  run: echo "${{ steps.version.outputs.value }}"

5.3 Step conditions

- name: Production-only step
  if: github.ref == 'refs/heads/main'
  run: ./publish.sh

5.4 Step environment

- name: Test
  env:
    API_URL: https://api.example.com
  run: npm test

5.5 Failure and continue-on-error

- name: Optional scan
  continue-on-error: true
  run: ./non-blocking-scan.sh

Use this carefully. It changes failure semantics and can hide defects.

A better pattern is to make the intent visible:

- name: Experimental compatibility test
  id: experimental
  continue-on-error: true
  run: npm run test:experimental

- name: Report experimental result
  if: always()
  run: echo "Experimental result = ${{ steps.experimental.outcome }}"

5.6 Timeouts

- name: External integration test
  timeout-minutes: 10
  run: npm run test:integration

Also set job timeouts:

jobs:
  test:
    timeout-minutes: 20

Production workflows should generally avoid the default six-hour job timeout unless the workload truly needs it.

5.7 Shells

Common shells:

shell: bash
shell: sh
shell: pwsh
shell: cmd
shell: python

Custom shell:

shell: python {0}

Example:

- name: Python inline script
  shell: python
  run: |
    import platform
    print(platform.platform())

5.8 Exit codes

For most shells:

  • exit 0 = success;
  • non-zero = failure.

Bash strict mode is often useful:

- name: Robust script
  run: |
    set -euo pipefail
    ./build.sh

But understand what it means:

OptionMeaning
-estop on failing command
-uerror on undefined variables
-o pipefailpipeline fails if any component fails

6. ESSENTIALS — Runners

6.1 What is a runner?

A runner is the machine or compute environment that executes a job.

GitHub Actions supports:

  1. GitHub-hosted runners;
  2. larger GitHub-hosted runners;
  3. self-hosted runners;
  4. Kubernetes-based ephemeral runners using Actions Runner Controller.
flowchart TD
    J[Queued job] --> M{Matching runner?}
    M -->|GitHub managed| H[Hosted runner]
    M -->|Large managed| L[Larger runner]
    M -->|Customer managed| S[Self-hosted runner]
    M -->|Kubernetes| A[ARC runner pod]
    H --> X[Execute job]
    L --> X
    S --> X
    A --> X

6.2 runs-on

Basic:

runs-on: ubuntu-latest

Specific image:

runs-on: ubuntu-26.04

Multiple self-hosted labels:

runs-on: [self-hosted, linux, x64, prod]

Expression:

runs-on: ${{ matrix.os }}

6.3 GitHub-hosted runners

Advantages:

  • fresh VM for each job;
  • no runner maintenance;
  • preinstalled tooling;
  • strong isolation between jobs;
  • simple scaling.

Trade-offs:

  • ephemeral filesystem;
  • public-network orientation unless private networking is configured;
  • image contents change over time;
  • fixed resource classes for standard runners.

2026 note

Current GitHub-hosted runner labels include modern images such as ubuntu-26.04 and newer macOS/Windows images. *-latest means GitHub’s latest stable supported runner image, not necessarily the newest operating-system version published by the OS vendor.

6.4 Larger runners

Use larger runners when you need:

  • more CPU/RAM/disk;
  • GPU;
  • autoscaling managed by GitHub;
  • runner groups;
  • static IP ranges;
  • supported private networking options;
  • custom images where available.

Decision example:

RequirementStandard hostedLarger runnerSelf-hosted / ARC
Zero maintenanceExcellentExcellentLow
Static egress IPPoorStrongStrong
Private networkLimited/optionsStronger optionsFull control
Custom base imageLimitedSupported in eligible configsFull
GPUSpecialized/limitedYesYes
Strong internal isolation controlMediumMedium/HighHighest control
Cost predictabilitySimplePer-minuteInfra + operations

6.5 Self-hosted runners

Self-hosted runners are customer-operated machines.

They can live on:

  • bare metal;
  • VM;
  • cloud instance;
  • on-premises server;
  • container/ephemeral VM;
  • Kubernetes via ARC.

Architecture

flowchart LR
    G[GitHub Actions service] <-->|Outbound HTTPS| R[Self-hosted runner]
    R --> C[Repository checkout]
    R --> P[Private package registry]
    R --> D[Database]
    R --> K[Kubernetes/API]
    R --> A[Cloud APIs]

Normally, the runner initiates outbound connections to GitHub. You do not generally expose an inbound runner control port to the internet.

6.6 Persistent vs ephemeral

Persistent runner

The same host runs multiple jobs over time.

Risks:

  • leftover files;
  • credentials not cleaned correctly;
  • modified toolchains;
  • poisoned caches;
  • malicious persistence.

Ephemeral runner

Created for one job and destroyed afterward.

Benefits:

  • clean environment;
  • less state leakage;
  • easier trust boundaries;
  • safer autoscaling architecture.

For sensitive production or untrusted workloads, ephemeral runners are usually preferable.

6.7 Runner groups

Runner groups control which repositories can use groups of runners.

Example organizational model:

Runner Groups
├── public-ci
├── internal-ci
├── production-deploy
└── privileged-infra

Use groups to enforce trust boundaries. A production deployment runner should not normally be available to every repository.

6.8 Private networking

Typical methods include:

  • self-hosted runner in a VPC/VNet;
  • larger runner private networking where supported;
  • hosted runner private-network connectivity patterns such as supported overlay/API-gateway approaches;
  • proxy;
  • VPN/overlay;
  • private endpoints.

Use case: private EKS deployment

flowchart LR
    G[GitHub] --> R[Ephemeral self-hosted runner<br/>private subnet]
    R --> E[EKS private API]
    R --> ECR[ECR]
    R --> S[Secrets manager]

The runner has network reachability. GitHub itself does not need direct access to the private cluster.

6.9 Runner security checklist

  • Prefer ephemeral runners.
  • Segment runner networks by trust level.
  • Do not allow untrusted public-repository PRs onto privileged self-hosted runners.
  • Avoid storing long-lived secrets on runner disks.
  • Patch runner OS and runner application.
  • Restrict metadata-service/cloud-instance credentials.
  • Restrict outbound network access where practical.
  • Use short-lived cloud credentials via OIDC.
  • Monitor runner registration and removal.
  • Remove stale runners.
  • Use runner groups and repository allowlists.

7. ESSENTIALS — Variables and Environment Variables

7.1 Three major variable mechanisms

MechanismSyntaxSensitive?Typical use
Environment variableenv: / $NAMENoprocess configuration
Configuration variable${{ vars.NAME }}Noreusable repo/org/env configuration
Secret${{ secrets.NAME }}Yestokens, passwords, keys

7.2 env scopes

Workflow:

env:
  APP_NAME: catalog

Job:

jobs:
  build:
    env:
      MODE: ci

Step:

- run: echo "$MODE"
  env:
    MODE: debug

The more specific scope overrides the broader scope.

7.3 Configuration variables

Examples:

env:
  API_URL: ${{ vars.API_URL }}

Variables can be configured at organization, repository, or environment level.

A lower-level configuration variable generally takes precedence over a higher-level one, but remember that environment-level variables become available after the job starts and do not behave exactly like pre-run env resolution in every location.

7.4 Default variables

Useful defaults include:

CI
GITHUB_ACTION
GITHUB_ACTION_PATH
GITHUB_ACTION_REPOSITORY
GITHUB_ACTIONS
GITHUB_ACTOR
GITHUB_ACTOR_ID
GITHUB_API_URL
GITHUB_BASE_REF
GITHUB_ENV
GITHUB_EVENT_NAME
GITHUB_EVENT_PATH
GITHUB_GRAPHQL_URL
GITHUB_HEAD_REF
GITHUB_JOB
GITHUB_OUTPUT
GITHUB_PATH
GITHUB_REF
GITHUB_REF_NAME
GITHUB_REF_PROTECTED
GITHUB_REF_TYPE
GITHUB_REPOSITORY
GITHUB_REPOSITORY_ID
GITHUB_REPOSITORY_OWNER
GITHUB_RETENTION_DAYS
GITHUB_RUN_ATTEMPT
GITHUB_RUN_ID
GITHUB_RUN_NUMBER
GITHUB_SERVER_URL
GITHUB_SHA
GITHUB_STEP_SUMMARY
GITHUB_TRIGGERING_ACTOR
GITHUB_WORKFLOW
GITHUB_WORKFLOW_REF
GITHUB_WORKFLOW_SHA
GITHUB_WORKSPACE
RUNNER_ARCH
RUNNER_ENVIRONMENT
RUNNER_NAME
RUNNER_OS
RUNNER_TEMP
RUNNER_TOOL_CACHE

Example

- name: Metadata
  run: |
    echo "repository=$GITHUB_REPOSITORY"
    echo "sha=$GITHUB_SHA"
    echo "ref=$GITHUB_REF"
    echo "runner=$RUNNER_OS/$RUNNER_ARCH"

7.5 Runtime-created environment variables

- name: Calculate
  run: echo "RELEASE_VERSION=2.4.0" >> "$GITHUB_ENV"

- name: Use
  run: echo "$RELEASE_VERSION"

The creating step does not receive the newly written value; later steps do.


8. ESSENTIALS — Contexts

8.1 Contexts vs environment variables

Contexts are evaluated by GitHub during workflow processing.

Environment variables are typically expanded by the shell on the runner.

Example:

- run: echo "${{ github.repository }}"

GitHub expression.

- run: echo "$GITHUB_REPOSITORY"

Shell expansion.

8.2 Major contexts

ContextContains
githubevent, repository, ref, SHA, actor, workflow metadata
envworkflow/job/step env values
varsconfiguration variables
jobcurrent job status/container/service metadata
stepsstep outcomes and outputs
runnerrunner OS, arch, temp, tool cache
secretssecrets available to the job
strategymatrix strategy metadata
matrixcurrent matrix combination
needsdependency job results and outputs
inputsmanual/reusable workflow inputs
jobscalled-workflow job outputs in supported reusable-workflow contexts

8.3 Property access

Dot notation:

${{ github.repository }}

Index notation:

${{ github['repository'] }}

Index notation is useful when keys are dynamic or contain characters unsuitable for dot notation.

8.4 Context debugging

Safer targeted debugging:

- name: Debug selected GitHub values
  run: |
    echo "event=${{ github.event_name }}"
    echo "ref=${{ github.ref }}"
    echo "sha=${{ github.sha }}"

Avoid casually dumping:

${{ toJSON(github) }}

because the github context can contain sensitive information such as tokens.

8.5 Context availability

Not every context is available in every workflow key.

For example:

  • matrix exists only for matrix jobs;
  • needs exists when jobs declare dependencies;
  • steps exists during step execution;
  • secrets are unavailable in some untrusted/fork contexts.

When GitHub reports:

Unrecognized named-value

check whether the context is legal at that point in workflow evaluation.


9. ESSENTIALS — Expressions

9.1 Syntax

${{ expression }}

Example:

if: ${{ github.ref == 'refs/heads/main' }}

For many if: clauses, the outer ${{ }} is optional:

if: github.ref == 'refs/heads/main'

9.2 Operators

Common operators:

!
>
>=
<
<=
==
!=
&&
||

Example:

if: github.ref == 'refs/heads/main' && !cancelled()

9.3 Useful functions

contains

if: contains(github.event.pull_request.labels.*.name, 'safe-to-deploy')

startsWith

if: startsWith(github.ref, 'refs/tags/v')

endsWith

if: endsWith(github.event.pull_request.title, '[release]')

format

env:
  IMAGE: ${{ format('{0}:{1}', vars.REGISTRY, github.sha) }}

join

${{ join(matrix.targets, ',') }}

toJSON

env:
  MATRIX_JSON: ${{ toJSON(matrix) }}

fromJSON

Turn JSON text into structured data:

strategy:
  matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}

hashFiles

key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

9.4 Status functions

if: success()
if: failure()
if: cancelled()
if: always()

Cleanup pattern

- name: Collect diagnostics
  if: failure()
  run: ./collect-diagnostics.sh

- name: Cleanup
  if: always()
  run: ./cleanup.sh

9.5 Expression security

Dangerous:

- run: echo "${{ github.event.pull_request.title }}"

A malicious title can become part of generated shell code.

Safer:

- name: Validate title
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: |
    printf '%s\n' "$PR_TITLE"

The key rule is:

Treat user-controlled event fields as untrusted data, not executable syntax.


10. ESSENTIALS — Secrets

10.1 Secret scopes

Secrets can exist at:

  • organization;
  • repository;
  • environment.

Use environment secrets for deployment-specific sensitive values.

10.2 Referencing a secret

- name: Login
  env:
    API_TOKEN: ${{ secrets.API_TOKEN }}
  run: ./login.sh

Prefer passing secrets through environment variables or action inputs rather than interpolating them directly into shell syntax.

10.3 Secret masking

GitHub attempts to mask known secrets from logs.

Do not assume masking is a data-loss-prevention system.

A secret can still leak if:

  • transformed/encoded;
  • embedded in another value;
  • written to an artifact;
  • sent to an external server;
  • exposed by malicious code;
  • stored on a persistent self-hosted runner.

10.4 Fork and Dependabot behavior

Untrusted pull requests and Dependabot-triggered workflows are intentionally restricted.

Do not design CI that requires powerful write credentials merely to test arbitrary PR code.

Architect separate trust zones:

flowchart LR
    A[Untrusted code] --> B[Read-only CI]
    B --> C[Test result / sanitized artifact]
    C --> D[Trusted workflow]
    D --> E[Privileged operation]

10.5 Secret rotation

A practical rotation pattern:

  1. issue new credential;
  2. add/update GitHub secret;
  3. validate a workflow;
  4. revoke old credential;
  5. review audit logs;
  6. document rotation date/owner.

Better still, eliminate many long-lived cloud secrets with OIDC.

10.6 Structured and large secrets

If an application needs structured secret material, prefer a secret manager or carefully encoded file material.

Example:

- name: Restore certificate
  env:
    CERT_B64: ${{ secrets.CERT_B64 }}
  run: |
    printf '%s' "$CERT_B64" | base64 --decode > certificate.p12

Delete sensitive files during cleanup, especially on self-hosted runners.


11. ESSENTIALS — GITHUB_TOKEN

11.1 What is GITHUB_TOKEN?

For each job, GitHub automatically creates a short-lived token that can authenticate to GitHub APIs and repository resources within the permissions granted to that job.

You do not create or rotate this token manually.

- name: Show authenticated user
  env:
    GH_TOKEN: ${{ github.token }}
  run: gh api user

In many cases, actions access it implicitly through:

${{ secrets.GITHUB_TOKEN }}

or:

${{ github.token }}

11.2 Permission model

Start with minimum privileges:

permissions:
  contents: read

Add only what is needed.

Example: create a GitHub Release:

permissions:
  contents: write

Example: cloud OIDC:

permissions:
  contents: read
  id-token: write

Example: artifact attestation:

permissions:
  contents: read
  id-token: write
  attestations: write

Common permissions

PermissionTypical reason
contentsclone/read/write repository contents and releases
actionswork with workflow runs/actions
checkscreate/update checks
deploymentsdeployment objects
discussionsdiscussions
id-tokenrequest OIDC token
issuesissues
packagesGitHub Packages / GHCR
pagesGitHub Pages
pull-requestsPR metadata/comments
security-eventsupload security results
statusescommit statuses
attestationsartifact attestations
artifact-metadatalinked/artifact metadata capabilities where used

11.3 Workflow vs job permissions

Workflow default:

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: npm test

  release:
    permissions:
      contents: write
    runs-on: ubuntu-latest
    steps:
      - run: gh release create ...

The release job gets extra permissions without granting them to the test job.

11.4 Read-all / write-all

GitHub supports broad forms such as:

permissions: read-all

Avoid:

permissions: write-all

unless there is an unusually strong reason.

11.5 Token lifetime

The token is tied to a job and is short-lived. Do not treat it as a long-term credential or persist it outside the workflow.

11.6 Token-triggered workflow behavior

GitHub deliberately prevents many actions performed with the repository’s GITHUB_TOKEN from recursively creating uncontrolled workflow chains.

If you need deliberate cross-repository or broader automation, consider:

  • GitHub App token;
  • fine-grained PAT;
  • repository_dispatch;
  • reusable workflows;
  • workflow_run.

Choose the narrowest identity and permissions that solve the problem.


12. ESSENTIALS — Workflow Commands

Workflow commands let a running step communicate structured information back to GitHub Actions.

12.1 Environment files

Modern workflows use environment files instead of deprecated output commands.

File variablePurpose
GITHUB_ENVvalues for later steps
GITHUB_OUTPUTstep outputs
GITHUB_PATHadd directories to PATH
GITHUB_STEP_SUMMARYMarkdown job summary
GITHUB_STATEstate between action phases

12.2 GITHUB_ENV

- name: Set runtime value
  run: echo "BUILD_CHANNEL=stable" >> "$GITHUB_ENV"

- name: Use runtime value
  run: echo "$BUILD_CHANNEL"

12.3 GITHUB_OUTPUT

- name: Calculate digest
  id: digest
  run: echo "sha=$(sha256sum app.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"

- name: Print digest
  run: echo "${{ steps.digest.outputs.sha }}"

12.4 GITHUB_PATH

- name: Install internal tools
  run: |
    mkdir -p "$HOME/bin"
    cp ./tools/mycli "$HOME/bin/"
    echo "$HOME/bin" >> "$GITHUB_PATH"

Later steps can execute:

- run: mycli --version

12.5 GITHUB_STEP_SUMMARY

- name: Publish test summary
  run: |
    {
      echo "## Test Summary"
      echo ""
      echo "| Suite | Result |"
      echo "|---|---|"
      echo "| Unit | ✅ Passed |"
      echo "| Integration | ✅ Passed |"
    } >> "$GITHUB_STEP_SUMMARY"

This creates a human-readable summary in the Actions UI.

12.6 Annotations

- run: echo "::notice title=Build::Compilation started"
- run: echo "::warning file=app.js,line=8::Deprecated API"
- run: echo "::error file=app.js,line=42::Validation failed"

Use annotations to make actionable errors visible near code.

12.7 Log grouping

- name: Grouped diagnostics
  run: |
    echo "::group::Environment"
    env | sort
    echo "::endgroup::"

Be careful not to print secrets.

12.8 Masking

- name: Mask dynamic value
  run: |
    TOKEN="$(./generate-temporary-token.sh)"
    echo "::add-mask::$TOKEN"

Mask before printing or passing a dynamically generated sensitive value.


13. ESSENTIALS — Matrix Builds

13.1 Why matrices exist

A matrix expands one job definition into multiple jobs.

Use cases:

  • multiple OSs;
  • multiple runtime versions;
  • multiple architectures;
  • multiple components;
  • test permutations.

13.2 Basic matrix

jobs:
  test:
    strategy:
      matrix:
        node: [22, 24]

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node }}

      - run: npm ci
      - run: npm test

Produces two jobs.

13.3 Multi-dimensional matrix

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    node: [22, 24]

runs-on: ${{ matrix.os }}

Expansion:

JobOSNode
1Ubuntu22
2Ubuntu24
3Windows22
4Windows24

13.4 Include

Add special combinations:

strategy:
  matrix:
    node: [22, 24]
    include:
      - node: 25
        experimental: true

13.5 Exclude

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    node: [20, 22, 24]
    exclude:
      - os: windows-latest
        node: 20

13.6 fail-fast

strategy:
  fail-fast: false

Use false when you want all matrix results even after one fails.

Use true when later combinations are not worth running after a mandatory combination fails.

13.7 Experimental combinations

jobs:
  test:
    continue-on-error: ${{ matrix.experimental }}

    strategy:
      fail-fast: true
      matrix:
        node: [22, 24]
        experimental: [false]
        include:
          - node: 25
            experimental: true

Stable failures block the workflow. Experimental failures do not.

13.8 max-parallel

strategy:
  max-parallel: 4
  matrix:
    shard: [1, 2, 3, 4, 5, 6, 7, 8]

Useful when:

  • test infrastructure can handle only limited concurrency;
  • licenses are scarce;
  • a database would overload;
  • cost needs control.

13.9 Dynamic matrix

Producer:

jobs:
  prepare:
    runs-on: ubuntu-latest

    outputs:
      matrix: ${{ steps.m.outputs.matrix }}

    steps:
      - id: m
        run: |
          echo 'matrix={"service":["api","worker","frontend"]}' >> "$GITHUB_OUTPUT"

Consumer:

  build:
    needs: prepare

    strategy:
      matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}

    runs-on: ubuntu-latest

    steps:
      - run: echo "Building ${{ matrix.service }}"

This is a powerful monorepo pattern.


14. ESSENTIALS — Caching

14.1 Cache vs artifact

A cache is a performance optimization.

An artifact is a retained workflow output.

CharacteristicCacheArtifact
Main purposespeedtransfer/retain files
Correctness dependencyshould be optionalmay be part of pipeline
Key lookupyesname/ID
Typical contentpackage manager cachebinaries, reports, plans
Mutableeffectively replaced with new keysupload produces artifact
Security concernpoisoningsensitive data / provenance

Golden rule:

Your build should remain correct if every cache disappears.

14.2 Package-manager caching

Simple Node example:

- uses: actions/setup-node@v7
  with:
    node-version: "24"
    cache: npm

- run: npm ci

For many ecosystems, setup actions provide automatic dependency caching.

14.3 actions/cache

- name: Cache npm data
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      npm-${{ runner.os }}-

14.4 Cache-key design

A good key encodes the inputs that make cache contents valid:

ecosystem + OS + architecture + toolchain + dependency lock hash

Example:

key: >-
  npm-${{ runner.os }}-${{ runner.arch }}-node24-
  ${{ hashFiles('**/package-lock.json') }}

14.5 Cache poisoning

If untrusted code can write to a cache later consumed by a privileged workflow, an attacker may plant malicious content.

Mitigations:

  • do not let low-trust workflows write privileged caches;
  • use restore-only cache in untrusted workflows;
  • use trust-separated cache keys;
  • do not cache executable security-sensitive state unnecessarily.

14.6 cache-mode

Current GitHub Actions supports:

ModeRestoreSave
readYesNo
writeYesYes
write-onlyNoYes
noneNoNo

Example:

cache-mode: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/cache@v4
        with:
          path: ~/.npm
          key: npm-${{ hashFiles('**/package-lock.json') }}

Use read for low-trust PR validation unless there is a deliberate reason to save.


15. ESSENTIALS — Artifacts

15.1 What is an artifact?

Artifacts are files produced by workflows and stored by GitHub for later use.

Examples:

  • compiled binary;
  • package;
  • Terraform plan;
  • test report;
  • coverage;
  • logs;
  • SBOM;
  • deployment bundle.

15.2 Upload

- name: Upload package
  uses: actions/upload-artifact@v4
  with:
    name: app-package
    path: dist/
    retention-days: 14

15.3 Download

- name: Download package
  uses: actions/download-artifact@v5
  with:
    name: app-package
    path: dist/

15.4 Sharing between jobs

flowchart LR
    B[Build job] -->|upload| A[(Artifact)]
    A -->|download| T[Test job]
    A -->|download| D[Deploy job]

Example:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: web-dist
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v5
        with:
          name: web-dist
          path: dist/
      - run: ./deploy.sh dist/

15.5 Build once, deploy many

A mature pipeline builds one immutable artifact and promotes that exact artifact.

flowchart LR
    C[Commit] --> B[Build once]
    B --> A[(Immutable artifact)]
    A --> S[Staging]
    S --> U[UAT]
    U --> P[Production]

Do not rebuild from source separately for each environment unless that behavior is intentionally part of the release model.

15.6 Retention

Set retention based on purpose:

ArtifactExample retention
temporary test logs3–7 days
PR build7–14 days
release candidate30–90 days
formal releasepreferably durable package/release registry
audit/provenance evidenceper compliance requirements

16. ESSENTIALS — Concurrency

Concurrency prevents conflicting or wasteful simultaneous runs.

16.1 Cancel stale CI

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

If a developer pushes three commits quickly, old CI can be cancelled.

16.2 Serialize production deployments

concurrency:
  group: production
  queue: max

This keeps one production deployment active while allowing pending deployment runs to queue.

2026 note

Current syntax supports queue: max to retain multiple pending runs in the same concurrency group. It cannot be combined with cancel-in-progress: true.

16.3 Environment-specific groups

concurrency:
  group: deploy-${{ inputs.environment }}
  queue: max

Staging and production can progress independently, while each environment remains serialized.

16.4 Avoid accidental cross-workflow cancellation

Bad:

concurrency:
  group: main

Two different workflows can share the same repository-level concurrency group.

Better:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}

17. ESSENTIALS — Containers

17.1 Container jobs

A job can run inside a container:

jobs:
  test:
    runs-on: ubuntu-latest

    container:
      image: node:24-bookworm

    steps:
      - uses: actions/checkout@v6
      - run: node --version
      - run: npm ci
      - run: npm test

This makes the job environment more deterministic.

17.2 Container credentials

container:
  image: ghcr.io/my-org/ci-image:2026.09
  credentials:
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

17.3 Environment, volumes, ports, options

container:
  image: ubuntu:24.04
  env:
    LANG: C.UTF-8
  volumes:
    - ci-cache:/cache
  options: --cpus 2

Use only runner/container options supported by GitHub’s runner/container implementation.

17.4 Service containers

Service containers are companion services available during a job.

PostgreSQL:

jobs:
  integration:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: app
          POSTGRES_PASSWORD: test-password
          POSTGRES_DB: app_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U app -d app_test"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v6

      - name: Integration tests
        env:
          DATABASE_URL: postgresql://app:test-password@localhost:5432/app_test
        run: npm run test:integration

Other common services:

Redis
MySQL
MongoDB
RabbitMQ
Elasticsearch/OpenSearch

17.5 Container networking mental model

If the job itself runs directly on the runner and services expose ports:

job process -> localhost:mapped-port -> service

If the job runs in a job container, service containers are typically addressable through their service labels on the shared container network.


18. ESSENTIALS — Using Actions

18.1 What is an action?

An action is a reusable unit that runs inside a step.

- uses: actions/checkout@v6

Sources include:

  • GitHub official actions;
  • Marketplace/community actions;
  • private actions;
  • actions in the same repository.

18.2 Action references

Major tag:

uses: actions/checkout@v6

Specific tag:

uses: some-org/some-action@v2.3.1

Commit SHA:

uses: some-org/some-action@0123456789abcdef0123456789abcdef01234567

For third-party production actions, a reviewed full SHA is the strongest common pinning strategy.

18.3 Local actions

Modern same-repository reference:

- uses: $/.github/actions/setup-project

Traditional relative form:

- uses: actions/checkout@v6
- uses: ./.github/actions/setup-project

The $-rooted repository action reference avoids depending on the checked-out workspace path in supported GitHub.com workflows.

18.4 Third-party action review checklist

Before approving an action:

  • Who maintains it?
  • Is the source public and reviewable?
  • Is it widely used?
  • Is release provenance credible?
  • What permissions does it need?
  • Does it execute downloaded code?
  • Does it send data externally?
  • Is it pinned?
  • Are dependencies maintained?
  • Does it handle secrets?
  • Is there an equivalent first-party action?

18.5 Dependabot for Actions

A typical configuration:

version: 2

updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly

This helps keep action references current. Security review is still required.


19. ESSENTIALS — Reusable Automation

19.1 Reusable Workflows

Reusable workflows centralize full jobs.

Called workflow:

.github/workflows/reusable-ci.yml
name: Reusable CI

on:
  workflow_call:
    inputs:
      node-version:
        type: string
        required: false
        default: "24"

    secrets:
      npm-token:
        required: false

    outputs:
      package-name:
        description: Package artifact name
        value: ${{ jobs.build.outputs.package-name }}

jobs:
  build:
    runs-on: ubuntu-latest

    outputs:
      package-name: ${{ steps.meta.outputs.name }}

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v7
        with:
          node-version: ${{ inputs.node-version }}
          cache: npm

      - run: npm ci
      - run: npm test
      - run: npm run build

      - id: meta
        run: echo "name=web-${{ github.sha }}" >> "$GITHUB_OUTPUT"

Caller:

jobs:
  ci:
    uses: my-org/platform-workflows/.github/workflows/reusable-ci.yml@v3
    with:
      node-version: "24"
    secrets: inherit

19.2 Reusable workflow design

flowchart TB
    R1[Repo A] --> W[Central reusable workflow]
    R2[Repo B] --> W
    R3[Repo C] --> W
    W --> S[Standard CI behavior]

Good candidates:

  • language CI standards;
  • security scanning;
  • container build/publish;
  • Terraform plan/apply;
  • deployment logic;
  • compliance steps.

19.3 Inputs and secrets

Prefer explicit inputs for behavior:

inputs:
  environment:
    type: string
    required: true

Use explicit secret contracts when possible.

secrets: inherit is convenient, but it gives the called workflow access to all caller secrets available under the inheritance rules. Use it deliberately.

19.4 Outputs

Reusable workflows can expose workflow outputs so the caller can consume computed data.

Use them for:

  • artifact name;
  • image digest;
  • release version;
  • deployment URL;
  • plan status.

19.5 Nesting

Reusable workflows can call reusable workflows.

Do not create deeply tangled call graphs merely because nesting is allowed. Keep ownership and privilege boundaries obvious.

2026 note

Current GitHub documentation allows up to 10 levels of connected reusable workflows and up to 50 unique reusable workflows reachable from a top-level workflow file. Treat limits as version-sensitive and check current documentation when designing very large platforms.

19.6 Composite actions vs reusable workflows

NeedComposite actionReusable workflow
Reuse stepsExcellentIndirectly
Reuse entire jobsNoYes
Choose runnerNoYes
Job permissionsNoYes
Environment gatesNoYes
Matrix jobsNoYes
Simple setup routineExcellentOverkill
Full CI standardLimitedExcellent

19.7 Workflow templates

Organization workflow templates help repositories bootstrap approved workflow structures.

Use templates when:

  • repositories need a standardized starting point;
  • teams may customize afterward;
  • a central reusable workflow is not enough by itself.

20. ESSENTIALS — CI Pipelines

20.1 A production CI pipeline

A mature CI pipeline commonly performs:

flowchart LR
    C[Checkout] --> I[Install]
    I --> L[Lint]
    I --> U[Unit tests]
    I --> S[Security]
    L --> B[Build]
    U --> B
    S --> B
    B --> A[Artifact]

20.2 Node.js example

name: Pull Request CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

cache-mode: read

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  quality:
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v7
        with:
          node-version: "24"
          cache: npm

      - run: npm ci

      - name: Lint
        run: npm run lint

      - name: Unit tests
        run: npm test -- --ci

      - name: Build
        run: npm run build

      - name: Upload build
        uses: actions/upload-artifact@v4
        with:
          name: web-${{ github.sha }}
          path: dist/
          retention-days: 7

20.3 Integration-test job

  integration:
    needs: quality
    runs-on: ubuntu-latest
    timeout-minutes: 20

    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: app
          POSTGRES_PASSWORD: app
          POSTGRES_DB: test
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U app -d test"
          --health-interval 5s
          --health-timeout 5s
          --health-retries 10

    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v7
        with:
          node-version: "24"
          cache: npm
      - run: npm ci
      - run: npm run test:integration
        env:
          DATABASE_URL: postgresql://app:app@localhost:5432/test

20.4 Required checks

Configure branch protection/rulesets so critical CI jobs must succeed before merge.

Examples:

lint
unit
integration
security
build

The exact enforcement belongs in repository rules/rulesets—not merely in workflow YAML.


21. ESSENTIALS — CD Pipelines

21.1 Delivery vs deployment

A good CD design separates:

  1. build — create deployable artifact;
  2. verify — prove artifact quality;
  3. promote — choose where it goes;
  4. deploy — apply change;
  5. verify deployment — prove runtime health;
  6. rollback — recover if needed.
flowchart LR
    C[Commit] --> B[Build]
    B --> T[Test]
    T --> A[(Artifact)]
    A --> D[Development]
    D --> S[Staging]
    S --> G{Approval / protection}
    G --> P[Production]
    P --> V[Smoke / health tests]
    V -->|Fail| R[Rollback]

21.2 Basic staged deployment

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: ./build.sh
      - uses: actions/upload-artifact@v4
        with:
          name: release
          path: dist/

  staging:
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/download-artifact@v5
        with:
          name: release
      - run: ./deploy.sh staging

  production:
    needs: staging
    runs-on: ubuntu-latest
    environment: production
    concurrency:
      group: production
      queue: max
    steps:
      - uses: actions/download-artifact@v5
        with:
          name: release
      - run: ./deploy.sh production

Environment settings, not the YAML alone, should enforce production approval/protection.

21.3 Rollback patterns

Rollback can mean:

  • redeploy previous artifact;
  • revert Git commit;
  • restore Kubernetes ReplicaSet;
  • switch blue/green target;
  • reduce canary traffic to zero;
  • revert Terraform/IaC change with a reviewed plan.

The rollback design must match the deployment technology. “Run deploy again” is not a rollback strategy by itself.


22. ESSENTIALS — Environments and Deployments

22.1 What is a GitHub environment?

An environment models a deployment target such as:

development
staging
uat
production

It can hold:

  • environment variables;
  • environment secrets;
  • allowed branches/tags;
  • required reviewers;
  • wait timers;
  • custom deployment protection rules;
  • deployment history.

22.2 Reference an environment

Simple:

environment: production

With URL:

environment:
  name: production
  url: https://app.example.com

Dynamic:

environment:
  name: ${{ inputs.environment }}

22.3 Environment secrets

jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest

    steps:
      - env:
          API_KEY: ${{ secrets.API_KEY }}
        run: ./deploy.sh

Environment secrets do not become available to the job until the applicable environment protections are satisfied.

22.4 Required reviewers

A production environment can require human approval.

flowchart LR
    J[Deployment job] --> W[Waiting]
    W --> A{Approved?}
    A -- No --> X[Rejected / timeout]
    A -- Yes --> R[Runner starts]
    R --> S[Environment secrets available]
    S --> D[Deploy]

22.5 Wait timers

A wait timer can delay deployment after triggering.

Use cases:

  • controlled release window;
  • soak period;
  • progressive delivery delay.

22.6 Branch/tag restrictions

Allow only appropriate refs to deploy:

production:
  protected branches only

or selected patterns according to repository policy.

22.7 Custom deployment protection rules

GitHub Apps can gate a deployment using external systems such as:

  • observability;
  • change-management approval;
  • security systems;
  • quality gates.

A sophisticated flow:

flowchart LR
    A[Deploy requested] --> G[GitHub environment gate]
    G --> O[Observability / ITSM / security app]
    O -->|Approve| D[Deployment]
    O -->|Reject| F[Stop]

22.8 Environment without deployment object

Current workflow syntax can use an environment’s secrets/variables and protections without creating a deployment record:

environment:
  name: staging
  deployment: false

This can be useful for CI or tests that need protected environment configuration but are not actual deployments.

Custom GitHub-App deployment protection rules require a deployment object, so they are not compatible with deployment: false.

22.9 Environment concurrency

Environments do not automatically solve every deployment race. Add concurrency:

concurrency:
  group: deploy-${{ inputs.environment }}
  queue: max

22.10 Recommended environment model

EnvironmentApprovalSecretsConcurrencyTypical source
developmentnolow privilegeoptionalfeature/main
stagingoptionalstaging-onlyserializemain
UAToftenUAT-onlyserializepromoted artifact
productionyes/protection ruleproduction-onlyserializeapproved release

Part II — Advanced Engineering

23. ADVANCED — Custom Actions

Custom actions package repeatable behavior behind a stable interface.

Use a custom action when repeated workflow steps represent a reusable capability rather than a full pipeline.

23.1 Types of custom actions

GitHub supports three major custom-action models:

TypeRuns onStrengthsTrade-offs
JavaScript actionLinux/macOS/WindowsFast startup, cross-platformMust package dependencies
Docker actionLinuxComplete runtime controlContainer startup, Linux only
Composite actionRunner shell/actionsEasy step reuseLess control than JS/Docker

23.2 Action metadata

An action directory contains:

.github/actions/my-action/
├── action.yml
└── ...

Typical metadata:

name: Setup Application
description: Install and prepare the application

inputs:
  node-version:
    description: Node.js version
    required: false
    default: "24"

outputs:
  cache-key:
    description: Effective dependency cache key
    value: ${{ steps.cache-key.outputs.value }}

runs:
  using: composite
  steps:
    - uses: actions/setup-node@v7
      with:
        node-version: ${{ inputs.node-version }}

    - id: cache-key
      shell: bash
      run: |
        echo "value=npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}" \
          >> "$GITHUB_OUTPUT"

Use:

- uses: $/.github/actions/setup-application
  with:
    node-version: "24"

23.3 JavaScript action

Project:

hello-action/
├── action.yml
├── package.json
├── src/
│   └── index.js
└── dist/
    └── index.js

action.yml:

name: Hello Action
description: Demonstrates a JavaScript action

inputs:
  who:
    description: Person to greet
    required: true

outputs:
  message:
    description: Generated greeting

runs:
  using: node24
  main: dist/index.js

src/index.js:

const core = require("@actions/core");

try {
  const who = core.getInput("who", { required: true });
  const message = `Hello, ${who}!`;

  core.info(message);
  core.setOutput("message", message);
} catch (error) {
  core.setFailed(error.message);
}

Package the dependency tree:

npm install
npx @vercel/ncc build src/index.js -o dist

The committed dist/ bundle makes the action executable without running npm install in the consumer workflow.

Toolkit modules

Common packages include:

@actions/core
@actions/github
@actions/exec
@actions/io
@actions/tool-cache

Use @actions/github for authenticated GitHub API access and Octokit.

23.4 Pre/main/post actions

Actions can have setup and cleanup phases.

Conceptually:

flowchart LR
    P[pre] --> M[main] --> O[post]

Use state when the post phase needs information created earlier.

Examples:

  • start/stop a service;
  • mount/unmount;
  • acquire/release temporary resource;
  • login/logout.

23.5 Docker action

action.yml:

name: Policy Check
description: Run a policy tool in a controlled container

inputs:
  path:
    description: Path to scan
    required: true

runs:
  using: docker
  image: Dockerfile
  args:
    - ${{ inputs.path }}

Dockerfile:

FROM alpine:3.22

RUN apk add --no-cache bash

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh:

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

target="$1"
echo "Scanning: $target"

A Docker action should write files that later steps need under the GitHub workspace mount.

23.6 Composite action

Composite actions are ideal for setup sequences.

name: Python Quality
description: Run standardized Python quality checks

inputs:
  python-version:
    required: false
    default: "3.14"

runs:
  using: composite
  steps:
    - uses: actions/setup-python@v5
      with:
        python-version: ${{ inputs.python-version }}

    - shell: bash
      run: pip install -r requirements-dev.txt

    - shell: bash
      run: ruff check .

    - shell: bash
      run: pytest

23.7 Versioning custom actions

For a shared action repository:

v1.0.0  immutable release
v1.1.0  immutable release
v1      moving major tag

Consumers seeking maximum supply-chain assurance can pin the release commit SHA.


24. ADVANCED — Workflow Data Flow

CI/CD design is mostly data movement under trust constraints.

24.1 Data-passing mechanisms

MechanismScopeBest for
Shell variablecurrent processtemporary command logic
GITHUB_ENVlater steps in same jobruntime configuration
Step outputlater steps/jobs via mappingsmall values
Job outputdependent jobssmall values
Reusable workflow outputcallerworkflow API contract
Artifactjobs/runsfiles, bundles, reports
Cachelater runsperformance data
External storearbitrary workflowsdurable shared state

24.2 Step → job → workflow

flowchart LR
    A[Step] -->|GITHUB_OUTPUT| B[Step output]
    B --> C[Job output]
    C -->|needs| D[Dependent job]
    C --> E[Reusable workflow output]
    E --> F[Caller workflow]

24.3 JSON as a workflow interchange format

Producer:

- id: components
  run: |
    echo 'value=["api","worker","frontend"]' >> "$GITHUB_OUTPUT"

Consumer:

strategy:
  matrix:
    component: ${{ fromJSON(needs.detect.outputs.components) }}

24.4 Artifact-based transfer

Use artifacts instead of outputs for large data.

Do not put these into outputs:

  • binary data;
  • large JSON documents;
  • Terraform plan files;
  • coverage directories;
  • container tarballs.

24.5 Trust classification

Before passing data downstream, classify it.

trusted configuration
trusted build output
untrusted PR metadata
untrusted PR artifact
external API response
user-supplied workflow_dispatch input

Do not treat “came from another job” as synonymous with “trusted.”


25. ADVANCED — Conditional Workflows

25.1 Event condition

if: github.event_name == 'push'

25.2 Branch condition

if: github.ref == 'refs/heads/main'

25.3 Tag condition

if: startsWith(github.ref, 'refs/tags/v')

25.4 Actor condition

if: github.actor == 'release-bot'

Use actor checks only as one signal. Do not treat username comparison as a substitute for permissions or environment protection.

25.5 Pull-request condition

if: github.event.pull_request.draft == false

25.6 Matrix condition

if: matrix.os == 'ubuntu-latest'

25.7 Fork detection

if: github.event.pull_request.head.repo.full_name == github.repository

This can separate same-repository PR behavior from fork behavior.

25.8 Failure-aware cleanup

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: ./test.sh

  collect:
    needs: test
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - run: ./collect-remote-logs.sh

25.9 Avoid unreadable conditions

Bad:

if: >
  (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
  (github.event_name == 'workflow_dispatch' && inputs.force == true) ||
  ...

Better approaches:

  • split jobs;
  • calculate a decision once;
  • use a reusable workflow;
  • keep policy in a script with tests.

A workflow should be readable during an incident.


26. ADVANCED — Error Handling

26.1 Failure hierarchy

command exit code
    ↓
step outcome/conclusion
    ↓
job conclusion
    ↓
dependent-job eligibility
    ↓
workflow conclusion

26.2 Step failure

- run: exit 1

Normally fails the job.

26.3 Controlled non-blocking failure

- id: optional-scan
  continue-on-error: true
  run: ./scan.sh

Later:

- if: steps.optional-scan.outcome == 'failure'
  run: echo "Optional scan failed"

26.4 Matrix failure

Use:

strategy:
  fail-fast: false

when every compatibility result matters.

26.5 Retry pattern

GitHub Actions does not mean every operation should be blindly retried.

Good retry candidates:

  • transient network request;
  • eventually consistent cloud API;
  • temporary package mirror error.

Bad retry candidates:

  • deterministic unit-test failure;
  • compiler error;
  • policy violation.

Shell example:

for attempt in 1 2 3; do
  if curl --fail --retry 2 https://service.example/health; then
    exit 0
  fi
  sleep $((attempt * 5))
done

exit 1

26.6 Rollback on failure

deploy:
  ...

verify:
  needs: deploy
  ...

rollback:
  needs: [deploy, verify]
  if: failure() && needs.deploy.result == 'success'
  ...

Automated rollback should be used only when rollback itself is well-tested and safe.

26.7 Cancellation-aware cleanup

if: always()

can execute after cancellation in some workflow evaluation paths, depending on where it is used.

For costly downstream jobs, often prefer:

if: ${{ !cancelled() }}

For local resource cleanup that truly must run, use action post steps or carefully designed cleanup steps.


27. ADVANCED — Troubleshooting and Debugging

Troubleshooting GitHub Actions is easier when you identify which layer failed.

flowchart TD
    A[Workflow not behaving] --> B{Did workflow start?}
    B -- No --> C[Trigger / branch / path / disabled workflow]
    B -- Yes --> D{Did job start?}
    D -- No --> E[if / needs / environment / runner queue]
    D -- Yes --> F{Did step start?}
    F -- No --> G[step if / previous failure]
    F -- Yes --> H{What failed?}
    H --> I[command/action]
    H --> J[permission/auth]
    H --> K[network/DNS]
    H --> L[cache/artifact]
    H --> M[service/container]

27.1 Workflow syntax errors

Symptoms:

  • workflow not listed;
  • invalid workflow file;
  • parser error.

Check:

  • indentation;
  • valid keys;
  • expression syntax;
  • unsupported context in a key;
  • correct .github/workflows path.

Use actionlint during development.

27.2 Trigger debugging

Ask:

  1. Did the event happen?
  2. Is the workflow file on the relevant branch/default branch?
  3. Does on match the event?
  4. Do branch filters match?
  5. Do path filters match?
  6. Does the event support the expected ref?
  7. Was the workflow disabled?
  8. Was the event created by a token behavior that intentionally does not trigger another workflow?

27.3 Context/expression debugging

Print only the properties you need:

- run: |
    echo "event=${{ github.event_name }}"
    echo "ref=${{ github.ref }}"
    echo "head=${{ github.head_ref }}"
    echo "base=${{ github.base_ref }}"

For structured debugging:

- env:
    MATRIX: ${{ toJSON(matrix) }}
  run: printf '%s\n' "$MATRIX"

Avoid whole-context dumps when tokens or secrets may be included.

27.4 Runner debugging

Print:

- run: |
    echo "OS=$RUNNER_OS"
    echo "ARCH=$RUNNER_ARCH"
    echo "ENV=$RUNNER_ENVIRONMENT"
    echo "WORKSPACE=$GITHUB_WORKSPACE"
    df -h
    free -h || true

Self-hosted runner checks:

runner online?
labels correct?
runner group allows repo?
network egress works?
runner version supported?
disk full?
service running?
workspace permissions?

27.5 Authentication failures

Differentiate:

401 -> identity/token invalid or absent
403 -> identity known but permission/policy denies
404 -> resource may be hidden by authorization or truly absent

Check:

  • workflow/job permissions;
  • environment approval;
  • fork restrictions;
  • token audience;
  • cloud trust policy;
  • repository access policy.

27.6 Network troubleshooting

Useful Linux commands:

getent hosts example.com
curl -v https://example.com
nc -vz host.example 443
ip route
cat /etc/resolv.conf

On self-hosted runners also check:

  • security groups/firewall;
  • proxy;
  • private DNS;
  • NAT;
  • route tables;
  • endpoint policies;
  • TLS interception.

27.7 Service container failures

Check:

  • image starts;
  • health check;
  • port mapping;
  • service hostname;
  • credentials;
  • startup time;
  • runner Docker capability.

27.8 Cache problems

Ask:

  • Is the key exactly what you expect?
  • Did hashFiles match any files?
  • Is cache-mode allowing restore/save?
  • Is the workflow low-trust?
  • Is the cache scoped to a different branch?
  • Is the cache stale by design?

27.9 Artifact problems

Check:

  • upload path exists;
  • hidden files behavior if relevant;
  • artifact name;
  • download path;
  • retention;
  • permissions for cross-run/cross-repo access.

27.10 Debug logging

Repository/organization settings can enable runner/step debug logging. Also use targeted verbose flags in your tools.

Do not enable excessive debug logging permanently on secret-heavy production workflows.

27.11 Re-runs

Understand:

GITHUB_RUN_ID       stable for the run
GITHUB_RUN_ATTEMPT  increases on re-run

A re-run can have a different triggering user, but privileges are based on GitHub’s documented actor behavior. Do not assume “person clicking re-run” automatically changes all permission semantics.


28. ADVANCED — Security Hardening

Security is not a final workflow step. It is part of the workflow’s execution model.

28.1 Trust boundaries

Classify workflow inputs and execution:

flowchart LR
    U[Untrusted PR / issue / external input] --> L[Low privilege]
    T[Trusted branch / release] --> H[Higher privilege]
    L --> V[Validation]
    H --> D[Deployment / publish]

28.2 Least privilege

Start:

permissions: {}

or:

permissions:
  contents: read

Then grant individual jobs what they need.

Example:

jobs:
  test:
    permissions:
      contents: read

  publish:
    permissions:
      contents: read
      packages: write
      id-token: write

28.3 Script injection

Dangerous:

- run: |
    echo "PR: ${{ github.event.pull_request.title }}"

An attacker controls the title.

Safer:

- env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: |
    printf 'PR: %s\n' "$PR_TITLE"

Best when practical: pass untrusted values as data to an action or program that does not interpret them as shell syntax.

28.4 pull_request vs pull_request_target

Propertypull_requestpull_request_target
Primary contextPR/merge codebase repository
Fork tokenrestricted/read-orientedprivileged base context
Secrets for fork PRwithheldpotentially available
Good for running PR codeYes, under low privilegeNo
Good for label/comment triagePossible with limitationsYes

Dangerous pattern

on:
  pull_request_target:

jobs:
  unsafe:
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ github.event.pull_request.head.sha }}

      - run: npm install
      - run: npm test

This combines privileged context with attacker-controlled code.

Use pull_request_target only when you understand and preserve the trust boundary.

28.5 Third-party action supply chain

A workflow action can:

  • read repository files;
  • read available secrets;
  • use the job token;
  • alter outputs;
  • make network requests.

Treat action code like any other dependency with execution privileges.

Production controls:

  • allowlist actions;
  • pin third-party actions by full SHA;
  • review source;
  • automate update PRs;
  • restrict job permissions;
  • isolate privileged jobs.

28.6 Self-hosted runner security

Never place public untrusted PR workloads on a persistent runner with access to:

  • production networks;
  • cloud instance role credentials;
  • internal systems;
  • signing keys;
  • persistent Docker sockets;
  • privileged Kubernetes service accounts.

Prefer ephemeral isolated runners.

28.7 Cache poisoning

Low-trust workflow:

cache-mode: read

Privileged trusted workflow:

cache-mode: write

Better: separate cache namespaces where trust levels differ.

28.8 Security baseline

name: Secure CI

on:
  pull_request:

permissions:
  contents: read

cache-mode: read

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - uses: actions/checkout@v6

      - name: Process untrusted title safely
        env:
          PR_TITLE: ${{ github.event.pull_request.title }}
        run: printf '%s\n' "$PR_TITLE"

29. ADVANCED — OpenID Connect

OIDC lets GitHub Actions obtain short-lived cloud credentials without storing a long-lived cloud access key in GitHub.

29.1 Traditional secret model

flowchart LR
    C[Cloud access key] --> S[GitHub secret]
    S --> W[Workflow]
    W --> C2[Cloud API]

Problems:

  • manual rotation;
  • duplicated long-lived credential;
  • blast radius if leaked.

29.2 OIDC model

sequenceDiagram
    participant W as GitHub Actions Job
    participant G as GitHub OIDC Provider
    participant C as Cloud STS/IAM
    participant R as Cloud Resource

    W->>G: Request OIDC ID token
    G-->>W: Signed short-lived ID token
    W->>C: Exchange token under trust policy
    C-->>W: Short-lived cloud credential
    W->>R: Authorized request

29.3 Workflow permission

OIDC requires:

permissions:
  contents: read
  id-token: write

id-token: write allows the job to request an ID token. It does not itself grant cloud resource access. The cloud-side trust policy decides what the token can assume.

29.4 Claims

Typical claims identify:

  • repository;
  • owner;
  • branch/ref;
  • tag;
  • environment;
  • workflow;
  • audience.

Cloud trust policy should be narrow.

29.5 AWS OIDC example

Workflow:

name: AWS Deploy

on:
  push:
    branches: [main]

permissions:
  contents: read
  id-token: write

jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@<PINNED_SHA>
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-production
          aws-region: ap-northeast-1

      - run: aws sts get-caller-identity

Illustrative AWS trust condition:

{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
    },
    "StringLike": {
      "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
    }
  }
}

Tighten the subject further to the exact branch or environment model you use.

AWS-specific point

AWS does not support GitHub OIDC custom claims in the same way some other providers do. Design AWS trust using supported standard claims and GitHub’s documented subject format.

29.6 Azure OIDC

Typical flow:

permissions:
  id-token: write
  contents: read

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

Azure federated credentials map GitHub identity claims to an Entra application or managed identity trust.

29.7 Google Cloud Workload Identity Federation

Typical flow:

permissions:
  contents: read
  id-token: write

steps:
  - uses: google-github-actions/auth@<PINNED_SHA>
    with:
      workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
      service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}

GCP validates GitHub OIDC assertions through Workload Identity Federation.

29.8 Vault and other providers

Any provider that can validate GitHub’s OIDC issuer and claims can use the model:

GitHub identity -> provider trust policy -> short-lived credential

29.9 2026 immutable subject claims

Current GitHub OIDC documentation includes immutable subject formats for newer/opted-in repositories using numeric owner/repository identifiers in addition to names. This reduces risks from repository renames/transfers.

When establishing new cloud trust in 2026+, inspect the actual sub format emitted for your repository and match the current GitHub OIDC reference rather than copying an old blog’s trust-policy string.

29.10 OIDC security checklist

  • grant id-token: write only to jobs that need it;
  • restrict cloud role subject;
  • restrict audience;
  • bind production roles to protected environments where appropriate;
  • use separate roles per environment;
  • keep cloud role permissions least-privilege;
  • use short session durations;
  • log role assumption;
  • never fall back to a long-lived key merely because OIDC trust configuration is inconvenient.

30. ADVANCED — Artifact Attestations

Artifact attestations establish verifiable information about where and how software was built.

30.1 Why provenance matters

A binary can be genuine only if you can answer:

Which repository?
Which workflow?
Which commit?
Which identity?
Which build process?
Was the artifact changed afterward?

30.2 Supply-chain flow

flowchart LR
    S[Source commit] --> B[GitHub Actions build]
    B --> A[Artifact / image]
    B --> P[Provenance attestation]
    A --> V[Verifier]
    P --> V
    V --> D{Policy satisfied?}
    D -- Yes --> R[Release / deploy]
    D -- No --> X[Reject]

30.3 Permissions

Attestation workflows commonly need:

permissions:
  contents: read
  id-token: write
  attestations: write

Package/container publishing also needs the relevant package permission.

30.4 Container provenance concept

- name: Build and push image
  id: push
  uses: docker/build-push-action@<PINNED_SHA>
  with:
    push: true
    tags: ghcr.io/my-org/my-app:${{ github.sha }}

- name: Attest image
  uses: actions/attest-build-provenance@<PINNED_SHA>
  with:
    subject-name: ghcr.io/my-org/my-app
    subject-digest: ${{ steps.push.outputs.digest }}
    push-to-registry: true

Use the exact current action/interface documented by GitHub when implementing; attestation features evolve.

30.5 Kubernetes enforcement

A Kubernetes admission policy can reject images without valid trusted provenance.

sequenceDiagram
    participant D as Deployment
    participant A as Admission Controller
    participant R as Registry/Attestation Store
    participant K as Kubernetes API

    D->>K: Create Pod
    K->>A: Admission review
    A->>R: Verify image provenance
    R-->>A: Attestation evidence
    A-->>K: Allow / deny

GitHub documents Sigstore Policy Controller integration for enforcing GitHub artifact attestations.

30.6 What attestations do not solve

Attestations do not prove:

  • source code is bug-free;
  • dependencies are safe;
  • workflow itself is secure;
  • maintainer intent is good.

They prove selected provenance/integrity properties. Pair them with code review, dependency security, pinning, branch protection, and isolated builds.


31. ADVANCED — Docker and Container CI/CD

31.1 Container pipeline

flowchart LR
    C[Code] --> T[Test]
    T --> B[Buildx / BuildKit]
    B --> I[Image]
    I --> S[Scan]
    I --> A[Attest/sign]
    S --> P[Push registry]
    A --> P
    P --> D[Deploy]

31.2 Buildx example

jobs:
  image:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      packages: write
      id-token: write
      attestations: write

    steps:
      - uses: actions/checkout@v6

      - uses: docker/setup-buildx-action@<PINNED_SHA>

      - uses: docker/login-action@<PINNED_SHA>
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - id: build
        uses: docker/build-push-action@<PINNED_SHA>
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

31.3 Image tagging

Use immutable tags:

sha-<commit>
release version
image digest

Do not rely on only:

latest

A robust model:

ghcr.io/org/app:sha-a1b2c3...
ghcr.io/org/app:v2.4.1
digest: sha256:...

Deploy by digest for strongest immutability.

31.4 Multi-platform

with:
  platforms: linux/amd64,linux/arm64

Multi-platform builds may use emulation or native builders. Measure build time and verify architecture-specific tests.

31.5 Docker layer caching

BuildKit cache can use suitable cache exporters/importers.

The exact cache design depends on:

  • builder topology;
  • trust model;
  • registry;
  • build size;
  • private dependencies.

Treat build cache as untrusted performance data unless you have a stronger provenance model.

31.6 Image scanning

A release path often includes:

dependency scan
Dockerfile lint
image vulnerability scan
SBOM
policy gate
provenance

Do not fail production simply because “scanner returned any CVE.” Define severity, exploitability, exception, and SLA policies.


32. ADVANCED — Package Publishing

GitHub Actions can publish:

  • npm;
  • Maven;
  • Gradle packages;
  • NuGet;
  • RubyGems;
  • container packages;
  • other registries.

32.1 npm publishing model

flowchart LR
    T[Tag/release] --> B[Build/test]
    B --> A[Package]
    A --> P[Registry]
    P --> C[Consumers]

Use trusted release triggers and protected publishing credentials.

32.2 GitHub Packages permissions

A package job may need:

permissions:
  contents: read
  packages: write

32.3 npm example

name: Publish npm

on:
  release:
    types: [published]

permissions:
  contents: read
  packages: write

jobs:
  publish:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v7
        with:
          node-version: "24"
          registry-url: https://npm.pkg.github.com

      - run: npm ci
      - run: npm test
      - run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

External registries may require registry-specific OIDC/trusted publishing or a secret.

32.4 Package provenance

Where supported, combine package publishing with provenance/attestation so consumers can verify build origin.


33. ADVANCED — Releases

33.1 Tag-driven release

on:
  push:
    tags:
      - "v*"

33.2 Release-event-driven

on:
  release:
    types: [published]

Choose based on ownership:

  • tag is source of truth;
  • GitHub Release publication is source of truth;
  • an external release manager triggers the workflow.

33.3 Semantic versioning

MAJOR.MINOR.PATCH

Examples:

1.4.3
2.0.0
2.1.0-rc.1

A release workflow should validate version format and prevent accidental republishing.

33.4 Release assets

permissions:
  contents: write

steps:
  - env:
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    run: |
      gh release upload \
        "${{ github.ref_name }}" \
        dist/app-linux-amd64 \
        dist/app-linux-arm64

33.5 Automated release architecture

flowchart LR
    M[Merge to main] --> V[Version decision]
    V --> T[Tag]
    T --> B[Build once]
    B --> A[Artifacts]
    A --> R[GitHub Release]
    A --> P[Package registry]
    R --> D[Deployment promotion]

Keep “build release artifact” separate from “deploy artifact” when possible.


34. ADVANCED — Cloud Deployments

Cloud deployments combine GitHub identity, cloud identity, network reachability, immutable artifacts, and deployment policy.

34.1 Cloud deployment reference architecture

flowchart TB
    G[GitHub repository] --> W[Workflow]
    W --> O[OIDC]
    O --> I[Cloud IAM / STS]
    I --> C[Short-lived credentials]
    C --> R[Cloud resource]
    W --> A[(Build artifact/image)]
    A --> R

34.2 AWS

Authentication

Preferred:

GitHub OIDC -> AWS IAM role -> temporary STS credentials

Avoid static AWS access keys in repository secrets when OIDC is available.

S3 deployment

- name: Sync static site
  run: aws s3 sync dist/ s3://my-site-bucket/ --delete

Give the role only the required bucket/object permissions.

EC2 deployment

Possible patterns:

  • SSM Run Command;
  • CodeDeploy;
  • immutable AMI + Auto Scaling;
  • instance pull from artifact store.

Avoid opening SSH from GitHub-hosted runner ranges merely to copy files to production.

ECR

- name: Login to ECR
  id: ecr
  uses: aws-actions/amazon-ecr-login@<PINNED_SHA>

Build/push, then deploy by immutable digest.

ECS

Typical flow:

build image -> push ECR -> update task definition -> deploy service -> wait stable

EKS

Typical flow:

OIDC -> IAM role -> EKS auth -> Helm/kubectl -> rollout verification

A private EKS endpoint requires a runner with network reachability into the VPC/network.

Lambda

Patterns:

  • zip artifact;
  • container image;
  • SAM/CDK/CloudFormation;
  • direct CLI for simple use cases.

CloudFormation

Production flow:

validate -> create change set -> review/gate -> execute -> monitor

Terraform

Production flow:

fmt -> validate -> security checks -> plan -> review/gate -> apply

Use cloud/OIDC auth and remote-state controls.

34.3 Azure

OIDC architecture:

GitHub OIDC -> Entra federated credential -> Azure identity -> resource

Targets:

  • Web Apps;
  • Container Apps;
  • AKS;
  • Functions;
  • Azure Storage;
  • Terraform.

Keep production identities/environment protections separate from test identities.

34.4 Google Cloud

Preferred model:

GitHub OIDC -> Workload Identity Federation -> service account -> GCP resource

Targets:

  • GKE;
  • Cloud Run;
  • Artifact Registry;
  • Cloud Storage;
  • Terraform.

34.5 Cloud deployment checklist

Before a production cloud workflow is considered complete:

  • [ ] no long-lived cloud key where OIDC can replace it;
  • [ ] cloud role restricted to exact repository/ref/environment;
  • [ ] production uses protected GitHub environment;
  • [ ] immutable artifact/image;
  • [ ] deployment serialized;
  • [ ] timeout configured;
  • [ ] smoke/health verification;
  • [ ] logs and deployment record;
  • [ ] rollback path;
  • [ ] least-privilege cloud IAM;
  • [ ] private-resource networking solved explicitly;
  • [ ] third-party actions pinned.

35. ADVANCED — Kubernetes CI/CD

GitHub Actions is commonly used to build immutable images and then deploy them to Kubernetes using kubectl, Helm, Kustomize, a GitOps system, or a cloud-specific deployment action.

35.1 Deployment model

flowchart LR
    C[Code] --> T[Test]
    T --> B[Build image]
    B --> R[Container registry]
    R --> D[Deploy manifest/Helm]
    D --> K[Kubernetes]
    K --> V[Rollout verification]
    V --> S[Smoke tests]

35.2 Authentication

Do not store a long-lived administrator kubeconfig in GitHub secrets if a stronger identity model is available.

Preferred patterns include:

  • cloud OIDC -> cloud IAM -> cluster authentication;
  • short-lived Kubernetes tokens;
  • workload identity;
  • GitOps pull model;
  • tightly scoped service accounts.

35.3 kubectl deployment

- name: Deploy
  run: |
    kubectl -n production set image \
      deployment/catalog \
      catalog="$IMAGE@$DIGEST"

    kubectl -n production rollout status \
      deployment/catalog \
      --timeout=5m

Use image digests when possible.

35.4 Helm

- name: Helm upgrade
  run: |
    helm upgrade --install catalog ./charts/catalog \
      --namespace production \
      --create-namespace \
      --set image.repository="$IMAGE_REPOSITORY" \
      --set image.digest="$IMAGE_DIGEST" \
      --wait \
      --timeout 5m

35.5 Kustomize

Typical flow:

kustomize build overlays/production | kubectl apply -f -

For GitOps, update an environment repository rather than applying directly.

35.6 Namespace isolation

Separate environments:

dev
staging
uat
production

But namespaces alone are not always enough isolation. Consider:

  • separate clusters/accounts/subscriptions;
  • network policies;
  • RBAC;
  • secret boundaries;
  • admission policy;
  • workload identity.

35.7 EKS

A secure EKS pattern:

flowchart LR
    G[GitHub job] --> O[GitHub OIDC]
    O --> I[AWS IAM role]
    I --> E[EKS authentication]
    G --> R[ECR]
    E --> K[EKS API]

Private cluster endpoint:

GitHub-hosted public runner -> cannot directly reach private endpoint

Possible solution:

ephemeral self-hosted runner / ARC inside VPC -> private EKS endpoint

35.8 AKS / GKE

Use Azure/GCP federated identity to obtain short-lived credentials, then obtain cluster credentials with the narrowest required scope.

35.9 Rollback

Kubernetes rollback depends on deployment model.

Imperative example:

kubectl rollout undo deployment/catalog -n production

Helm:

helm rollback catalog <revision> -n production

GitOps:

revert environment commit -> reconciler restores prior desired state

35.10 Deployment verification

A deployment is not complete when kubectl apply returns zero.

Verify:

  • rollout completed;
  • desired replicas available;
  • readiness healthy;
  • error rate stable;
  • smoke test passes;
  • critical dependencies healthy.

36. ADVANCED — Infrastructure as Code

IaC workflows deserve stricter controls than ordinary application CI because they can alter networks, IAM, databases, and production resources.

36.1 Terraform pipeline

flowchart LR
    C[Change] --> F[fmt]
    F --> V[validate]
    V --> S[Security/policy]
    S --> P[plan]
    P --> A[(Plan artifact)]
    A --> G{Approval}
    G --> X[apply]

36.2 Pull-request plan

name: Terraform PR

on:
  pull_request:
    paths:
      - "infra/**"
      - ".github/workflows/terraform-pr.yml"

permissions:
  contents: read
  id-token: write

jobs:
  plan:
    runs-on: ubuntu-latest
    timeout-minutes: 20

    defaults:
      run:
        working-directory: infra

    steps:
      - uses: actions/checkout@v6

      - name: Configure cloud credentials
        run: echo "Use provider-specific OIDC setup here"

      - name: Terraform fmt
        run: terraform fmt -check -recursive

      - name: Terraform init
        run: terraform init -input=false

      - name: Terraform validate
        run: terraform validate

      - name: Terraform plan
        run: terraform plan -input=false -out=tfplan

      - name: Upload plan
        uses: actions/upload-artifact@v4
        with:
          name: tfplan-${{ github.sha }}
          path: infra/tfplan
          retention-days: 5

36.3 Production apply

Production apply should normally require:

  • trusted source revision;
  • protected environment;
  • short-lived cloud identity;
  • state locking;
  • serialized execution;
  • reviewed plan;
  • timeout;
  • audit trail.
jobs:
  apply:
    environment: production

    concurrency:
      group: terraform-production
      queue: max

    permissions:
      contents: read
      id-token: write

    runs-on: self-hosted

    steps:
      - ...

36.4 Plan artifact caution

A saved Terraform plan can contain sensitive values depending on provider/resource behavior.

Treat plan artifacts as potentially sensitive:

  • short retention;
  • restricted repository access;
  • do not publish publicly;
  • avoid logging full plan if it exposes secrets.

36.5 Terraform Cloud

Two patterns:

GitHub Actions -> Terraform Cloud API -> remote execution

or:

GitHub Actions runner -> Terraform CLI -> Terraform Cloud remote state/execution

Use the model that fits governance and network access.

36.6 OpenTofu

Most workflow architecture is similar:

fmt -> init -> validate -> plan -> gate -> apply

Use OpenTofu-specific CLI/provider/module compatibility guidance.

36.7 CloudFormation / CDK

Recommended flow:

lint/synth
validate
security/policy scan
change set
review
execute
verify

36.8 Pulumi

Use stack-specific credentials/state and protected environments.

36.9 Drift detection

Scheduled workflow:

on:
  schedule:
    - cron: "30 1 * * *"

The job should report drift, not automatically destroy/recreate production infrastructure unless that behavior is intentionally governed.


37. ADVANCED — Self-Hosted Runner Architecture

A self-hosted runner fleet is infrastructure, not merely “a VM with the runner installed.”

37.1 Fleet goals

A production fleet should address:

  • trust boundaries;
  • elasticity;
  • image management;
  • patching;
  • credentials;
  • networking;
  • observability;
  • cleanup;
  • cost;
  • disaster recovery.

37.2 Dedicated vs shared

Dedicated:

one repository/team -> one runner pool

Shared:

many repositories -> common pool

Shared fleets have better utilization but a larger trust boundary.

37.3 Reference architecture

flowchart TB
    G[GitHub Actions] --> RG[Runner group]
    RG --> Q[Job queue]

    Q --> A1[Ephemeral runner A]
    Q --> A2[Ephemeral runner B]
    Q --> A3[Ephemeral runner C]

    subgraph PrivateNetwork[Private network]
      A1
      A2
      A3
      P[Private package registry]
      K[Kubernetes API]
      DB[Private database]
    end

    A1 --> P
    A2 --> K
    A3 --> DB

37.4 Golden images

A runner image can preinstall:

  • language runtimes;
  • Terraform/OpenTofu;
  • cloud CLIs;
  • kubectl/Helm;
  • organization CA certificates;
  • security tools.

Benefits:

  • faster jobs;
  • controlled versions;
  • repeatability.

Risks:

  • stale image;
  • vulnerable tooling;
  • snowflake modifications.

Version images immutably:

runner-2026.09.1
runner-2026.09.2

37.5 Just-in-time / ephemeral registration

A mature autoscaled system registers runners when capacity is needed and destroys them after one job.

Lifecycle:

flowchart LR
    Q[Job demand] --> P[Provision compute]
    P --> R[Register ephemeral runner]
    R --> J[Execute one job]
    J --> U[Upload logs/results]
    U --> D[Destroy compute]

37.6 Network access

Define egress intentionally.

Examples:

general CI pool:
  internet + package registries

internal CI pool:
  internal services + controlled internet

production deploy pool:
  production APIs + artifact registry
  no arbitrary repository access

37.7 Cloud metadata risk

If a self-hosted runner is on a cloud VM with an instance profile/managed identity, workflow code may be able to obtain those credentials.

Do not rely on “the workflow does not know the credential” as a security boundary.

Prefer:

  • no ambient privileged role;
  • short-lived explicit OIDC identity;
  • metadata restrictions;
  • isolated ephemeral compute.

37.8 Runner monitoring

Monitor:

  • registered count;
  • online/offline;
  • busy/idle;
  • queue time;
  • startup latency;
  • job duration;
  • failure rate;
  • disk;
  • CPU/memory;
  • runner version;
  • image version;
  • orphaned runners.

38. ADVANCED — Actions Runner Controller (ARC)

Actions Runner Controller is GitHub’s Kubernetes-oriented runner orchestration solution.

38.1 Mental model

flowchart LR
    G[GitHub Actions] <--> L[ARC listener]
    L --> C[Scale set controller]
    C --> K[Kubernetes API]
    K --> P1[Ephemeral runner pod]
    K --> P2[Ephemeral runner pod]
    K --> P3[Ephemeral runner pod]

A runner scale set represents a scalable pool of compatible runners.

38.2 Core components

Typical concepts include:

  • ARC/controller;
  • autoscaling runner scale set;
  • listener;
  • runner pods;
  • Kubernetes namespace;
  • authentication to GitHub;
  • runner image;
  • min/max runners.

38.3 Why ARC?

Use ARC when:

  • Kubernetes is already an operational platform;
  • workloads need private-network access;
  • runners should be ephemeral;
  • demand changes dynamically;
  • teams need isolated pools;
  • custom images are useful.

38.4 High-level installation flow

1. Prepare Kubernetes cluster.
2. Install ARC controller/chart.
3. Configure GitHub App/PAT authentication as supported.
4. Create runner scale set.
5. Set runner group/access policy.
6. Configure runner image and container mode.
7. Test job routing.
8. Add monitoring and upgrades.

Use the exact Helm chart/API versions from the current GitHub ARC documentation; ARC configuration evolves.

38.5 GitHub App vs PAT

For organization-scale production, GitHub App authentication is generally easier to govern and rotate than a broad personal token.

Evaluate:

  • permissions;
  • installation scope;
  • organization policy;
  • secret storage;
  • rotation;
  • rate limits.

38.6 Runner namespace isolation

Possible model:

arc-system
runners-general
runners-infra
runners-production

Use Kubernetes RBAC, network policies, pod security, node pools, and cloud identities to separate trust levels.

38.7 Custom runner image

A runner image should be reproducible.

FROM <supported-runner-base>

# Install pinned organization tools.
# Avoid baking long-lived secrets into the image.

Keep sensitive credentials external.

38.8 Docker-in-Docker

DinD can be useful for image builds but introduces:

  • privilege concerns;
  • storage overhead;
  • daemon lifecycle;
  • cache management.

Alternatives include:

  • BuildKit;
  • rootless builders;
  • Kubernetes-native build systems;
  • remote builders.

Choose based on your security model.

38.9 Kubernetes container mode

ARC can support container-oriented job execution patterns depending on the runner scale-set configuration.

Test:

  • filesystem behavior;
  • service containers;
  • Docker requirements;
  • volume access;
  • network policies.

38.10 Autoscaling

Set minimum and maximum based on:

expected concurrency
startup latency
cluster capacity
budget
burst tolerance

A pool with minRunners: 0 may scale to zero but has cold-start latency.

A nonzero minimum reduces queue latency at idle cost.

38.11 ARC observability

Track:

queued GitHub jobs
desired runners
active runner pods
pod startup time
pod failures
scheduling failures
listener health
controller reconciliation
Kubernetes resource pressure

38.12 ARC security

  • use ephemeral runner pods;
  • separate public/untrusted and privileged pools;
  • restrict pod service accounts;
  • restrict network access;
  • do not mount cluster-admin credentials;
  • avoid privileged Docker socket exposure when possible;
  • apply resource limits;
  • use dedicated nodes for sensitive pools if required;
  • keep ARC and runner images patched.

39. ADVANCED — Workflow Performance

Fast feedback changes developer behavior. Slow CI is not merely inconvenient.

39.1 Measure first

Important timings:

queue time
runner startup
checkout
dependency installation
build
tests
artifact upload
deployment

39.2 Critical path

flowchart LR
    A[Checkout 10s] --> B[Install 60s]
    B --> C[Build 90s]
    B --> D[Unit 45s]
    B --> E[Lint 20s]
    C --> F[Integration 120s]
    D --> F
    E --> F

Optimizing a 20-second lint that is not on the critical path may not improve total latency.

39.3 Parallelize independent jobs

Instead of:

lint -> unit -> security -> build

use:

       /-> lint
setup -> unit
       \-> security
       \-> build

when the tasks are truly independent.

39.4 Cache dependencies

Cache package manager download caches, not blindly the entire installed dependency directory.

Examples:

  • npm cache;
  • pip cache;
  • Maven local repository;
  • Gradle cache.

39.5 Checkout optimization

If full history is unnecessary, default shallow checkout is efficient.

For special cases:

with:
  fetch-depth: 0

only when history/tags are needed.

Sparse checkout can reduce large monorepo I/O.

39.6 Selective execution

Use:

  • trigger paths;
  • changed-component detection;
  • dynamic matrices;
  • test-impact analysis.

Do not skip cross-cutting validation that protects shared code merely to gain speed.

39.7 Runner sizing

A bigger runner helps only when the task can use the resources.

Measure:

CPU saturation?
memory pressure?
disk I/O?
network?
single-threaded bottleneck?

39.8 Image pre-baking

Self-hosted/larger custom images can preinstall heavyweight tools to reduce repeated setup.

Balance speed against image maintenance complexity.


40. ADVANCED — Cost Optimization

Actions cost is a combination of compute time, storage, and engineering overhead.

40.1 Cost equation

Simplified:

CI cost =
  workflow frequency
  × jobs per run
  × average duration
  × runner rate
  + artifact/cache storage
  + self-hosted infrastructure/operations

40.2 Biggest levers

  1. cancel stale PR runs;
  2. avoid unnecessary trigger events;
  3. reduce matrix permutations;
  4. cache expensive dependencies;
  5. parallelize to reduce wall clock—but understand total compute may stay equal or increase;
  6. lower artifact retention;
  7. use appropriate runner sizes;
  8. run expensive scans at the right cadence;
  9. use path-aware monorepo workflows;
  10. measure before migrating to self-hosted merely for cost.

40.3 Matrix cost explosion

This:

os: [ubuntu, windows, macos]
node: [20, 22, 24]
db: [postgres, mysql, sqlite]

creates:

3 × 3 × 3 = 27 jobs

Ask whether every combination provides unique value.

40.4 Cost-aware compatibility strategy

PR:

primary OS + supported runtime versions

Nightly:

full OS/runtime compatibility matrix

Release:

all required release targets

40.5 Self-hosted economics

Self-hosted is not “free.”

Include:

  • compute;
  • idle capacity;
  • Kubernetes cluster;
  • storage;
  • network;
  • image building;
  • patching;
  • security;
  • on-call;
  • monitoring;
  • autoscaling engineering.

41. ADVANCED — Monorepo Workflows

Monorepos require selective execution without losing confidence in shared dependencies.

41.1 Naive model

any file changed -> build every service

Simple but expensive.

41.2 Path-filter model

on:
  pull_request:
    paths:
      - "services/api/**"
      - "packages/shared/**"

Good for independent components but limited when dependencies are complex.

41.3 Changed-component detection

flowchart LR
    D[Git diff] --> C[Changed files]
    C --> M[Map files -> components]
    M --> G[Expand dependency graph]
    G --> J[Dynamic matrix]
    J --> B[Build affected components]

41.4 Dynamic component matrix

Prepare job:

outputs:
  matrix: ${{ steps.detect.outputs.matrix }}

Example output:

{
  "component": ["api", "worker"]
}

Build:

strategy:
  matrix: ${{ fromJSON(needs.detect.outputs.matrix) }}

41.5 Shared library impact

If:

services/api -> packages/core
services/worker -> packages/core

a change to packages/core should build/test both services.

Path filters alone cannot fully represent a dependency graph unless manually encoded.

41.6 Independent deployments

api change -> deploy API
frontend change -> deploy frontend
shared change -> deploy affected services

Each component can have its own:

  • artifact;
  • image;
  • version;
  • environment;
  • concurrency group.

41.7 Monorepo cache keys

Include component identity:

key: npm-${{ matrix.component }}-${{ hashFiles(format('{0}/package-lock.json', matrix.component)) }}

Avoid one giant cache that creates contention and invalidates constantly.


42. ADVANCED — Multi-Repository Workflows

Organizations often centralize CI policy while keeping application code in separate repositories.

42.1 Patterns

Central reusable workflows

flowchart TB
    A[service-a] --> C[central-workflows]
    B[service-b] --> C
    D[service-c] --> C

Cross-repository dispatch

Repo A -> API / repository_dispatch -> Repo B workflow

Central deployment repository

app repositories -> build artifacts
deployment repository -> environment promotion

42.2 repository_dispatch

Caller using GitHub CLI/API conceptually:

gh api \
  --method POST \
  repos/my-org/deployer/dispatches \
  -f event_type=deploy-request \
  -f 'client_payload[service]=catalog' \
  -f 'client_payload[version]=2.4.1'

Receiver:

on:
  repository_dispatch:
    types: [deploy-request]

42.3 Authentication

GITHUB_TOKEN is repository-scoped and is intentionally limited for cross-repository actions.

Use an appropriately scoped:

  • GitHub App installation token;
  • fine-grained PAT;
  • supported organization/reusable-workflow access policy.

Prefer GitHub Apps for machine-to-machine organization automation when practical.

42.4 Cross-repo artifacts

Common solutions:

  • package registry;
  • container registry;
  • release assets;
  • object storage;
  • APIs.

Avoid fragile “download another repo’s workflow artifact by guessing its latest run” designs unless the run identity is deterministic and verified.


43. ADVANCED — Workflow Chaining

43.1 workflow_run

Use chaining when workflows need different privileges.

on:
  workflow_run:
    workflows: ["Build"]
    types: [completed]

43.2 Security boundary

A strong pattern:

flowchart LR
    A[PR workflow<br/>untrusted, read-only] --> B[Artifact/result]
    B --> C[workflow_run<br/>trusted workflow]
    C --> D[Privileged publish/comment]

But the privileged workflow must validate every artifact/input created by the untrusted workflow.

43.3 Avoid excessive chains

Bad:

workflow A
 -> B
 -> C
 -> D
 -> E
 -> F

Problems:

  • harder traceability;
  • fragmented logs;
  • latency;
  • failure handling complexity;
  • privilege confusion.

Prefer one workflow with job dependencies when a single trust boundary is sufficient.


44. ADVANCED — GitHub API Integration

GitHub Actions can automate GitHub itself using REST, GraphQL, gh, Octokit, or github-script.

44.1 gh

- name: Comment on issue
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    gh issue comment "${{ github.event.issue.number }}" \
      --body "Automation completed."

44.2 gh api

- name: Read repository
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: gh api "repos/${GITHUB_REPOSITORY}"

44.3 actions/github-script

- uses: actions/github-script@<PINNED_SHA>
  with:
    script: |
      await github.rest.issues.createComment({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
        body: 'CI completed.'
      })

44.4 REST vs GraphQL

RESTGraphQL
simple resource operationsquery related data efficiently
familiar endpointsselect exact fields
easy with gh apiuseful for complex org/report queries
pagination by endpointquery cost model

44.5 Checks and status APIs

Use when building custom CI integrations, but ordinary Actions jobs already produce checks automatically.

44.6 Actions API

Useful automation:

  • list runs;
  • re-run jobs;
  • cancel runs;
  • download logs;
  • manage artifacts/caches;
  • inspect runners.

44.7 Rate limits

Large organizations should design API automation with:

  • pagination;
  • conditional requests;
  • caching;
  • backoff;
  • GitHub App installation tokens;
  • rate-limit monitoring.

45. ADVANCED — Repository Governance

Workflows are enforceable only when repository governance requires them.

45.1 Governance layers

flowchart TB
    R[Repository rules/rulesets] --> B[Branch/tag policy]
    R --> S[Required status checks]
    R --> M[Merge policy]
    R --> C[CODEOWNERS/reviews]
    R --> A[Actions settings]
    R --> E[Environment protections]

45.2 Branch protection / rulesets

Typical main-branch policy:

  • require PR;
  • require approvals;
  • require code-owner review;
  • require status checks;
  • dismiss stale approvals if desired;
  • block force pushes;
  • restrict deletion;
  • require signed commits where policy demands;
  • require linear history if desired;
  • merge queue for high-volume repositories.

45.3 Required status checks

Names must remain stable enough for rules to target them.

Good:

jobs:
  unit:
    name: Unit Tests

Do not casually rename required jobs across hundreds of repositories without updating governance.

45.4 Merge queue and merge_group

Repositories using merge queues should ensure required CI runs against the merge group event.

Example:

on:
  pull_request:
  merge_group:

Without merge-group support, a required workflow may not validate the exact queued merge combination.

45.5 CODEOWNERS

CODEOWNERS decides review ownership. Actions can support it but should not replace branch/ruleset enforcement with homemade scripts.

45.6 Actions repository settings

Govern:

  • allowed actions;
  • workflow permissions;
  • fork workflow policy;
  • reusable workflow access;
  • runner access.

45.7 Environments as governance

Production controls belong in both:

repository source rules
+
environment deployment rules

A merge approval and a deployment approval answer different questions.


46. ADVANCED — Organization Governance

Organization-level policy reduces inconsistent security across repositories.

46.1 Policy areas

  • allowed GitHub Actions;
  • third-party action restrictions;
  • SHA pinning policies where available;
  • reusable workflow access;
  • organization secrets;
  • organization variables;
  • runner groups;
  • workflow permissions;
  • retention;
  • fork workflow policy.

46.2 Organization action policy

Possible strategy:

Allow:
  GitHub-owned actions
  selected verified actions
  organization-owned actions
  explicitly reviewed third-party actions

This is stronger than letting every repository use arbitrary Marketplace code.

46.3 Organization secrets

Use org secrets for truly shared credentials, with repository access limited.

Do not put a powerful enterprise credential into an organization secret visible to all repositories merely for convenience.

46.4 Organization variables

Good for non-sensitive standards:

DEFAULT_AWS_REGION
ARTIFACT_RETENTION_DAYS
INTERNAL_REGISTRY
PLATFORM_TEAM

46.5 Runner groups

Example:

general-linux
mobile-macos
internal-network
production-deploy
terraform-private

Grant repositories to groups intentionally.

46.6 Central workflow governance

A platform repository can own:

reusable-ci.yml
container-build.yml
terraform-plan.yml
terraform-apply.yml
deploy-kubernetes.yml
security-scan.yml

Application repositories call reviewed versions.

This creates a reusable paved road without copying thousands of lines of YAML.

46.7 Governance principle

Centralize policy and common capability, not every repository-specific decision.

A reusable workflow should expose intentional inputs rather than becoming a 100-input universal pipeline.

47. ADVANCED — Enterprise Governance

Enterprise governance applies policy consistently across organizations while preserving controlled delegation.

47.1 Enterprise control plane

flowchart TB
    E[Enterprise policy] --> O1[Organization A]
    E --> O2[Organization B]
    E --> O3[Organization C]

    O1 --> R1[Repositories]
    O2 --> R2[Repositories]
    O3 --> R3[Repositories]

    E --> RG[Enterprise runner groups]
    E --> AU[Audit / usage]

47.2 Enterprise policy areas

Common controls:

  • Actions usage policy;
  • allowed/selected actions;
  • enterprise runner groups;
  • organization inheritance;
  • workflow permissions;
  • fork policies;
  • reusable workflow strategy;
  • audit logging;
  • billing/usage analysis;
  • identity and access.

47.3 Policy inheritance

A lower level should not be able to silently weaken an enterprise security requirement.

Think in layers:

Enterprise maximum allowed capability
  ↓
Organization policy
  ↓
Repository policy
  ↓
Workflow/job least privilege

A repository can usually choose to be more restrictive, but should not be able to exceed organization/enterprise boundaries.

47.4 Enterprise runner groups

Useful model:

Enterprise runner groups
├── general
├── regulated
├── production
├── mobile
└── high-memory

Each organization gets access only to the groups it needs.

47.5 Audit logs

Audit:

  • workflow policy changes;
  • runner changes;
  • secret/configuration changes;
  • repository settings;
  • environment approvals;
  • organization/enterprise Actions settings;
  • authentication events.

Audit data is useful only when retained, searchable, and connected to incident response.

47.6 Enterprise operating model

A mature model usually separates:

RoleResponsibility
Enterprise adminsplatform-wide policy
Platform teamreusable workflows/runners
Securitycontrols, review, detection
Org ownersorganization policy
Repo maintainersrepository CI/CD
App teamsapplication-specific logic
Release approversproduction authorization

48. ADVANCED — Workflow Execution Policies

Execution policy answers:

Which workflows may execute, under which identities, events, repositories, and protections?

48.1 Policy dimensions

  • actor;
  • event;
  • repository;
  • action source;
  • runner group;
  • permissions;
  • environment;
  • fork status;
  • branch/tag.

48.2 Defense in depth

flowchart LR
    E[Event] --> P1[Enterprise/Org policy]
    P1 --> P2[Repository Actions policy]
    P2 --> W[Workflow trigger/conditions]
    W --> T[Token permissions]
    T --> R[Runner access]
    R --> G[Environment gate]
    G --> X[Privileged action]

No single layer should be expected to carry all authorization.

48.3 Actor restrictions

Avoid implementing sensitive authorization as:

if: github.actor == 'alice'

This is brittle and not a substitute for:

  • repository permissions;
  • teams;
  • environment reviewers;
  • GitHub Apps;
  • protected branches;
  • cloud IAM.

Actor checks can supplement, not replace, policy.

48.4 Event restrictions

Examples:

PR -> test only
push main -> build/publish candidate
release -> publish release
workflow_dispatch -> controlled operational action
schedule -> maintenance

Tie higher privileges to trusted events and protected refs.


49. ADVANCED — Metrics and Observability

A CI/CD platform should be operated with SLO-like thinking.

49.1 Core metrics

Workflow metrics

run count
success rate
failure rate
cancellation rate
duration
queue duration
re-run rate

Job metrics

duration by job
failure by job
runner wait time
matrix expansion
timeout count

Runner metrics

online runners
busy runners
utilization
queue depth
startup latency
job pickup latency
failure rate
version/image drift

Cost metrics

minutes by repository
minutes by runner type
artifact storage
cache storage
top expensive workflows

49.2 CI service-level indicators

Example internal objectives:

SLIExample target
PR CI p50< 5 min
PR CI p95< 15 min
Queue p95< 2 min
Platform-caused failure< 1%
Production deployment success> 99%
Runner availability> 99.9%

These are examples, not universal targets.

49.3 Failure classification

Do not treat all red workflows the same.

Classify:

code defect
test defect/flaky test
workflow defect
runner/platform
dependency registry
network
cloud provider
permission/policy
deployment target

Without classification, teams optimize the wrong problem.

49.4 Observability dashboard

flowchart TB
    A[Actions APIs / audit logs] --> D[Data pipeline]
    R[Runner metrics] --> D
    B[Billing/usage] --> D
    D --> M[Metrics store]
    M --> V[Dashboard]
    M --> AL[Alerts]

49.5 Alerting

Good alerts indicate action:

runner pool unable to accept jobs
production deployment failure
queue time above SLO
reusable workflow failure spike
authentication failures

Bad alerts notify on every ordinary developer test failure to a central on-call.


50. ADVANCED — Workflow Notifications

Notifications should route information to the people who can act.

50.1 Native notifications

GitHub can notify users through:

  • web;
  • email;
  • repository/activity notification mechanisms.

50.2 Chat notifications

Possible integrations:

  • Slack;
  • Microsoft Teams;
  • custom webhook;
  • incident-management tool.

50.3 Notification job

notify:
  needs: [build, test, deploy]
  if: failure()
  runs-on: ubuntu-latest

  steps:
    - name: Build notification payload
      env:
        RUN_URL: >-
          ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
      run: |
        echo "Workflow failed: $RUN_URL"

Send via a reviewed integration or webhook.

50.4 Avoid secret webhooks when apps can use scoped auth

A long-lived chat webhook is a bearer credential. Store and scope it appropriately.

50.5 Notification design

PR failure:

GitHub check is usually enough

Production failure:

GitHub + deployment channel + incident path if impact exists

Scheduled maintenance failure:

team-owned alert

51. ADVANCED — Workflow Cancellation

Cancellation is a normal workflow state and should be designed for.

51.1 Cancellation sources

  • user clicks Cancel;
  • concurrency cancels older run;
  • API cancellation;
  • timeout;
  • organization/platform policy.

51.2 Concurrency cancellation

concurrency:
  group: pr-${{ github.event.pull_request.number }}
  cancel-in-progress: true

51.3 Cleanup

If a workflow creates temporary infrastructure, design cleanup even when cancellation occurs.

Options:

  • action post phase;
  • cleanup step/job;
  • TTL on temporary resources;
  • external janitor workflow;
  • idempotent resource naming.

51.4 Preview environment example

flowchart LR
    P[PR opened/update] --> C[Create/update preview]
    P --> X[Older run cancelled]
    X --> J[Cleanup/TTL safety]
    Q[PR closed] --> D[Destroy preview]

Never rely on only the “PR closed” workflow for cleanup. Events can fail; build a TTL/janitor.

51.5 Signals

Processes should handle termination where practical.

Containerized/server steps should not require an unbounded graceful shutdown.


52. ADVANCED — Pull Request CI Patterns

PR CI is the highest-volume and often lowest-trust workflow surface.

52.1 Baseline PR workflow

name: PR CI

on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]

permissions:
  contents: read

cache-mode: read

concurrency:
  group: pr-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  ci:
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - uses: actions/checkout@v6
      - run: ./ci.sh

52.2 Draft PRs

Options:

  • skip expensive tests until ready;
  • run only lint/unit;
  • run full CI if draft PRs are routinely used for collaboration.

Make behavior explicit.

52.3 Fork PRs

Assume:

  • code is untrusted;
  • repository secrets are not available;
  • write token is restricted;
  • self-hosted privileged runners are dangerous.

52.4 PR comments

Do not switch the whole test workflow to pull_request_target merely to post a comment.

Safer pattern:

pull_request -> test -> artifact/result
workflow_run -> trusted commenter

Validate the result artifact before use.

52.5 Changed-file workflows

Use path filtering for coarse optimization and dependency-aware detection for complex monorepos.

52.6 Preview environments

A secure preview system needs:

  • isolated namespace/account;
  • no production secrets;
  • bounded lifetime;
  • safe ingress;
  • predictable DNS;
  • cleanup;
  • cost limit;
  • untrusted input handling.

52.7 Merge queue

If required checks are used with merge queue:

on:
  pull_request:
  merge_group:

Validate the queued merge group, not only the original PR head.


53. ADVANCED — Branch and Release Strategies

GitHub Actions should implement your source-control/release strategy, not accidentally define one.

53.1 Trunk-based development

feature branch -> PR -> main -> deploy

Good fit for:

  • small changes;
  • strong automation;
  • feature flags;
  • frequent release.

53.2 GitFlow-style

feature -> develop -> release -> main
                    \-> hotfix

More branches and workflow states mean more automation complexity.

Use only if the organizational release model actually needs it.

53.3 Release branches

Example:

main
release/2.4
release/2.5

Actions can validate/patch supported release lines.

53.4 Environment branches

A branch-per-environment model can work but couples source history to deployment state.

Alternative:

immutable artifact + environment promotion metadata

Often easier to reason about.

53.5 Tag-based release

on:
  push:
    tags:
      - "v*"

Validate that the tag points to an approved commit lineage if required.

53.6 Hotfix workflow

A hotfix should still preserve:

review
tests
artifact integrity
production protection
post-deploy verification

“Emergency” should not automatically mean “no controls.”


54. ADVANCED — Deployment Strategies

54.1 Rolling deployment

Replace instances gradually.

flowchart LR
    O[Old replicas] --> M[Mixed old/new]
    M --> N[All new]

Pros:

  • simple;
  • resource efficient.

Risk:

  • mixed versions coexist.

54.2 Blue/green

flowchart LR
    U[Users] --> R{Router}
    R --> B[Blue active]
    R -. switch .-> G[Green new]

Deploy green, verify, switch traffic. Rollback by switching back while blue remains viable.

54.3 Canary

flowchart LR
    U[Traffic] --> S{Traffic split}
    S -->|95%| O[Old]
    S -->|5%| N[New]
    N --> M[Metrics]
    M -->|Healthy| I[Increase traffic]
    M -->|Bad| R[Rollback]

Requires observability and traffic control.

54.4 Progressive delivery

Canary is one form. A progression can use:

1% -> 5% -> 25% -> 50% -> 100%

Each stage can gate on:

  • error rate;
  • latency;
  • saturation;
  • business metric;
  • manual approval.

54.5 Feature flags

Deployment and release become separate:

deploy code disabled
-> enable feature for internal users
-> 1%
-> 10%
-> all

Flags need lifecycle management; stale flags are operational debt.

54.6 Immutable deployment

Promote a previously built artifact/image rather than changing it per environment.

Strong model:

same digest -> staging -> UAT -> production

54.7 Smoke tests

After deploy:

- name: Smoke
  run: |
    curl --fail --retry 5 --retry-delay 5 \
      https://app.example.com/health

A /health endpoint alone may not represent user-critical functionality. Include targeted synthetic smoke checks when needed.


55. ADVANCED — Testing GitHub Actions

Workflows are production code and should be tested.

55.1 Validation layers

YAML parser
workflow linter
shell/script tests
custom action unit tests
reusable workflow tests
integration repository
staging deployment
production canary

55.2 actionlint

actionlint catches many:

  • syntax;
  • expressions;
  • shellcheck-related issues;
  • context mistakes.

Example local/CI use:

actionlint

55.3 YAML validation

A generic YAML parser can catch syntax but not GitHub-specific semantic errors.

Use both generic syntax validation and Actions-aware linting.

55.4 JavaScript action testing

Separate business logic from Actions runtime adapters.

Example architecture:

src/
  logic.js       <- pure/testable
  action.js      <- @actions/core wrapper

Unit test logic.js, then integration-test action.js.

55.5 Composite action testing

Create a small workflow that invokes the action with:

  • default inputs;
  • custom inputs;
  • failing inputs;
  • expected outputs;
  • Linux/Windows/macOS if supported.

55.6 Reusable workflow testing

A reusable workflow should have a test caller.

test repository/workflow -> call reusable workflow -> assert outputs/artifacts

55.7 Local emulation with act

act can be useful for fast local feedback but is not an exact GitHub-hosted runner implementation.

Differences may include:

  • images;
  • permissions;
  • networking;
  • services;
  • GitHub API behavior;
  • environment protection;
  • OIDC;
  • hosted-runner tooling.

Use it as a development aid, not final proof.

55.8 Staging workflow tests

For risky platform changes:

branch/ref of reusable workflow
-> test repository
-> staging environment
-> production version tag

Do not immediately move a central v1 tag across hundreds of repositories without canary validation.


56. ADVANCED — Workflow Maintenance

A workflow that was secure and correct two years ago may no longer be.

56.1 Maintenance inventory

Review:

  • action versions;
  • deprecated actions;
  • runner images;
  • runtime versions;
  • third-party dependencies;
  • secrets;
  • OIDC trust;
  • runner application;
  • container images;
  • reusable workflow versions.

56.2 Dependabot for GitHub Actions

version: 2

updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly

Use review and test before merging updates.

56.3 Runner image changes

Avoid relying on accidental preinstalled software.

Explicitly set up critical versions:

- uses: actions/setup-node@v7
  with:
    node-version: "24"

56.4 Node runtime changes for actions

JavaScript actions target runner-supported Node action runtimes.

When GitHub deprecates an action runtime, update custom actions and third-party dependencies.

56.5 Refactoring triggers

Refactor when you see:

  • copy/paste across repositories;
  • 500-line monolithic workflows;
  • duplicated setup steps;
  • unclear privilege boundaries;
  • repeated shell scripts embedded in YAML;
  • enormous boolean conditions.

56.6 Secret rotation

Track:

owner
purpose
created
last rotated
expires
repositories
replacement plan

Prefer OIDC to reduce long-lived cloud secrets.

56.7 Documentation

Each production workflow should make it easy to answer:

What triggers this?
What does it change?
What identity does it use?
What is the artifact?
What protects production?
How do I retry?
How do I roll back?
Who owns it?

57. ADVANCED — GitHub Actions Limits

Limits change over time. Build scalable systems from current GitHub documentation rather than memorizing an old blog.

57.1 Important current examples

As of this handbook verification date, GitHub documents limits including:

  • workflow-run lifetime;
  • environment approval waiting;
  • matrix expansion;
  • concurrency/queue behavior;
  • reusable workflow depth/count;
  • API limits;
  • runner/job limits;
  • storage limits.

Matrix

A matrix can generate at most 256 jobs per workflow run in the current documented limit.

Workflow run

Current documentation lists a workflow run maximum lifetime of 35 days, including execution and waiting/approval time.

Environment approval

A workflow can wait for environment approval for up to 30 days under the current documented limit.

Reusable workflows

Current reference documents:

up to 10 levels
up to 50 unique reusable workflows reachable from one caller

57.2 Design principle

Do not design at the exact hard limit.

If you need:

250 matrix jobs every PR

the issue is probably architectural before it is a quota issue.

57.3 Common scale failures

  • giant dynamic matrix;
  • thousands of repos calling one bottleneck service;
  • API polling without backoff;
  • artifact retention explosion;
  • excessive cross-workflow chaining;
  • too-small runner pool;
  • limited private endpoint capacity.

58. ADVANCED — GitHub Actions Importer

GitHub Actions Importer helps migrate CI/CD definitions from supported systems.

58.1 Migration is not translation only

flowchart LR
    A[Existing CI] --> I[Inventory]
    I --> C[Convert]
    C --> R[Review]
    R --> T[Test]
    T --> S[Security redesign]
    S --> P[Production cutover]

A converted workflow can be syntactically correct but architecturally poor.

58.2 Typical migration sources

Examples include:

  • Jenkins;
  • GitLab CI/CD;
  • CircleCI;
  • Azure DevOps;
  • Travis CI.

58.3 Migration inventory

Record:

pipeline trigger
secrets
service connections
agents/runners
artifacts
caches
environments
approvals
shared libraries
plugins
notifications
deployment targets

58.4 Replace platform-specific concepts

Examples:

Jenkins shared library -> reusable workflow/composite action
Jenkins agent label -> runs-on labels/runner group
GitLab artifact -> Actions artifact
Azure environment approval -> GitHub environment protection
CI cloud service connection -> OIDC + cloud IAM

58.5 Migration security opportunity

Do not copy:

  • long-lived cloud keys;
  • administrator tokens;
  • shared mutable workers;
  • insecure shell interpolation;
  • unpinned third-party code.

Use migration as a security modernization.


59. ADVANCED — Common CI/CD Design Patterns

59.1 Build-test-deploy

flowchart LR
    B[Build] --> T[Test] --> D[Deploy]

Simple and appropriate for many applications.

59.2 Build once, deploy many

flowchart LR
    C[Commit] --> B[Build]
    B --> A[(Artifact)]
    A --> S[Stage]
    S --> U[UAT]
    U --> P[Prod]

Recommended for immutable promotion.

59.3 Fan-out/fan-in

flowchart LR
    B[Build] --> U[Unit]
    B --> I[Integration]
    B --> S[Security]
    U --> G[Gate]
    I --> G
    S --> G

59.4 Matrix

One definition, many permutations.

Good for compatibility testing.

59.5 Central reusable pipeline

application repo
  -> central reusable CI
  -> central security
  -> central deployment

Useful for consistent organization standards.

59.6 Artifact promotion

Do not rebuild between environments.

Promote:

artifact ID
image digest
release bundle

59.7 Conditional deployment

if: github.ref == 'refs/heads/main'

Use conditions plus environment protections.

59.8 Manual approval gates

Human approval is appropriate when:

  • regulatory control;
  • production change review;
  • high-risk operation;
  • business timing decision.

It is not a substitute for automated verification.

59.9 Event-driven automation

release created -> publish
issue labeled -> triage
deployment status -> notify
package published -> downstream test

59.10 Dynamic pipeline generation

GitHub workflow structure itself remains YAML-defined, but matrices and outputs can dynamically choose work.

Avoid generating arbitrary executable workflow logic from untrusted input.


60. ADVANCED — GitHub Actions Anti-Patterns

60.1 Duplicate workflow logic

Problem:

50 repositories × copied 200-line workflow

Fix:

reusable workflow + small caller

60.2 Overprivileged token

Bad:

permissions: write-all

Fix:

permissions:
  contents: read

and elevate only specific jobs.

60.3 Long-lived cloud credentials

Bad:

AWS_ACCESS_KEY_ID secret
AWS_SECRET_ACCESS_KEY secret

when OIDC is supported.

Fix:

OIDC -> scoped role -> short-lived token

60.4 Mutable third-party actions

Bad:

uses: unknown/action@main

Better:

uses: unknown/action@<reviewed-full-SHA>

60.5 Secrets in YAML

Never:

env:
  PASSWORD: real-password-here

60.6 Secrets in logs

Never enable shell tracing around secret-bearing commands without understanding exposure.

Avoid:

set -x

for secret-heavy scripts.

60.7 Untrusted input in shell

Bad:

run: echo "${{ github.event.issue.title }}"

Use environment indirection or safe structured code.

60.8 Unsafe pull_request_target

Never combine:

privileged base context
+
checkout attacker-controlled PR
+
execute PR code

60.9 Persistent public self-hosted runners

This creates a persistence and internal-network attack surface.

Use GitHub-hosted runners or isolated ephemeral runners for untrusted public workflows.

60.10 Excessive matrix

A matrix should represent valuable test dimensions, not every imaginable combination.

60.11 Cache misuse

Never make cache availability a correctness requirement.

Never let low-trust code poison privileged cache state.

60.12 Artifact misuse

Artifacts are not automatically secure secret stores.

Do not upload:

  • cloud credentials;
  • signing keys;
  • .env secrets;
  • kubeconfigs;
  • plaintext tokens.

60.13 Hard-coded environments

Bad:

aws s3 cp ... s3://prod-account-bucket

inside generic CI.

Use environment-specific variables and identities.

60.14 Excessive workflow chaining

Prefer job dependencies unless separate workflow trust boundaries are actually useful.

60.15 Monolithic workflow

Symptoms:

  • hundreds/thousands of lines;
  • unrelated products;
  • mixed privilege levels;
  • many nested conditions.

Split by responsibility and reuse stable building blocks.

60.16 Missing timeouts

A stuck external call can consume hours.

Set realistic job/step timeouts.

60.17 Missing concurrency

Concurrent production deployments can race.

Add environment-specific serialization.

60.18 Missing deployment protections

A YAML condition is not equivalent to protected environment policy.


61. ADVANCED — Production Best Practices

This chapter condenses the handbook into a production baseline.

61.1 Secure identity

  • least-privilege GITHUB_TOKEN;
  • OIDC for cloud;
  • separate environment roles;
  • no static cloud keys where federation works;
  • protected production environments.

61.2 Supply-chain controls

  • pin third-party actions by full SHA;
  • automate action update PRs;
  • scan dependencies;
  • generate SBOM/provenance as needed;
  • attest release artifacts;
  • verify before deployment.

61.3 Runner controls

  • GitHub-hosted for common untrusted CI;
  • ephemeral self-hosted/ARC for private workloads;
  • runner groups;
  • network segmentation;
  • patched golden images;
  • no ambient privileged credentials.

61.4 Workflow engineering

  • reusable workflows;
  • composite actions for repeated steps;
  • job timeouts;
  • concurrency;
  • path filtering;
  • caching;
  • build once/deploy many;
  • clear outputs;
  • readable conditions.

61.5 Repository governance

  • rulesets/branch protection;
  • required checks;
  • merge queue support;
  • CODEOWNERS;
  • restricted Actions policy;
  • protected environments.

61.6 Operational readiness

  • workflow metrics;
  • queue monitoring;
  • runner monitoring;
  • cost monitoring;
  • failure classification;
  • ownership;
  • rollback;
  • runbook;
  • auditability.

61.7 Production reference workflow

name: Production Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: read

concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false

jobs:
  verify:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    cache-mode: read

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v7
        with:
          node-version: "24"
          cache: npm

      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

      - uses: actions/upload-artifact@v4
        with:
          name: release-${{ github.sha }}
          path: dist/
          retention-days: 30

  deploy:
    needs: verify

    permissions:
      contents: read
      id-token: write

    environment:
      name: production
      url: https://app.example.com

    concurrency:
      group: production
      queue: max

    runs-on: ubuntu-latest
    timeout-minutes: 30

    steps:
      - uses: actions/download-artifact@v5
        with:
          name: release-${{ github.sha }}
          path: dist/

      - name: Obtain short-lived cloud credentials
        run: echo "Use provider OIDC action pinned to a reviewed SHA"

      - name: Deploy
        run: ./deploy.sh dist/

      - name: Verify
        run: ./smoke-test.sh https://app.example.com

The exact cloud/auth action should be pinned to a reviewed SHA.


62. ADVANCED — Reference and Administration

This section is a practical index of where to look when operating GitHub Actions.

62.1 Workflow reference

Use the official workflow syntax reference for:

  • valid keys;
  • contexts allowed in a key;
  • defaults;
  • concurrency;
  • matrices;
  • containers;
  • services;
  • reusable calls;
  • modern syntax such as cache access and parallel/background steps.

62.2 Events reference

Check the event reference for:

  • default activity types;
  • branch/ref behavior;
  • event payload;
  • default-branch requirements;
  • fork security behavior;
  • recursive-trigger limitations.

62.3 Workflow commands

Use for:

  • outputs;
  • environment variables;
  • PATH;
  • step summaries;
  • annotations;
  • masking;
  • action state.

62.4 Variables, expressions, and contexts

These three references answer different questions:

ReferenceQuestion
VariablesWhat environment/config variables exist?
ExpressionsWhat operators/functions can I evaluate?
ContextsWhat structured runtime data is available here?

62.5 Deployments and environments

Use for:

  • required reviewers;
  • wait timers;
  • deployment branches/tags;
  • custom protection rules;
  • environment variables/secrets;
  • deployment records.

62.6 Runners

Administrators should know:

  • standard hosted runner images;
  • larger runner capabilities;
  • self-hosted network requirements;
  • runner groups;
  • ARC;
  • image/runtime updates.

62.7 Security

Always keep current on:

  • secure-use reference;
  • pull_request_target;
  • secrets;
  • OIDC;
  • action pinning;
  • fork policy;
  • runner isolation;
  • cache security.

62.8 Limits

Check current limits before designing:

  • huge matrices;
  • deep reusable workflows;
  • long approvals;
  • extreme run durations;
  • high API volumes;
  • large artifacts/caches.

62.9 Billing and usage

Review:

  • included minutes;
  • hosted runner rates;
  • larger runner rates;
  • storage;
  • organization usage;
  • repository hot spots.

62.10 Administrative runbook

A GitHub Actions administrator should maintain:

Actions policy
runner inventory
runner group ownership
approved actions
reusable workflow catalog
secret inventory
OIDC trust inventory
environment inventory
cost dashboard
platform SLOs
upgrade cadence
incident runbook

Part III — End-to-End Tutorials and Labs

Lab A — From Zero to a Production-Quality Node.js CI Workflow

A.1 Repository

demo-app/
├── .github/
│   └── workflows/
│       └── ci.yml
├── src/
├── test/
├── package.json
└── package-lock.json

A.2 Requirements

We want:

  • PR and main validation;
  • least privilege;
  • stale-run cancellation;
  • Node 22 and 24 compatibility;
  • npm cache;
  • lint;
  • test;
  • build;
  • artifact;
  • clear summary.

A.3 Workflow

name: Application CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

cache-mode: read

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  quality:
    name: Node ${{ matrix.node }}
    runs-on: ubuntu-latest
    timeout-minutes: 15

    strategy:
      fail-fast: false
      matrix:
        node: [22, 24]

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Setup Node
        uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node }}
          cache: npm

      - name: Install
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Unit tests
        run: npm test -- --ci

      - name: Build
        run: npm run build

      - name: Summary
        if: always()
        run: |
          {
            echo "## Node ${{ matrix.node }}"
            echo ""
            echo "- Ref: \`${{ github.ref }}\`"
            echo "- SHA: \`${{ github.sha }}\`"
            echo "- Result: \`${{ job.status }}\`"
          } >> "$GITHUB_STEP_SUMMARY"

      - name: Upload production build
        if: matrix.node == 24
        uses: actions/upload-artifact@v4
        with:
          name: web-${{ github.sha }}
          path: dist/
          retention-days: 7

A.4 Why each control exists

ControlReason
contents: readCI should not need repository write
cache-mode: readPR code does not need to publish cache state
concurrencycancel obsolete CI
timeout-minutesbound failure/hang cost
fail-fast: falsecollect compatibility results
npm cilockfile-respecting deterministic install
artifact on Node 24 onlyavoid duplicate build artifacts

Lab B — Reusable Organization CI

B.1 Goal

Ten repositories should use the same quality gate without copying the workflow.

B.2 Central workflow

name: Standard Node CI

on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: "24"
      working-directory:
        type: string
        default: "."

jobs:
  test:
    runs-on: ubuntu-latest

    defaults:
      run:
        working-directory: ${{ inputs.working-directory }}

    permissions:
      contents: read

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v7
        with:
          node-version: ${{ inputs.node-version }}
          cache: npm
          cache-dependency-path: >-
            ${{ inputs.working-directory }}/package-lock.json

      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

B.3 Application caller

name: CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  ci:
    uses: my-org/platform-workflows/.github/workflows/node-ci.yml@v3
    with:
      node-version: "24"

B.4 Version promotion

feature branch in platform-workflows
-> test repositories
-> tag v3.1.0
-> move reviewed v3 major tag if your organization uses moving major tags
-> Dependabot/update consumers

For high-assurance consumers, pin the called workflow to a full commit SHA.


Lab C — Secure AWS OIDC Deployment

C.1 Architecture

sequenceDiagram
    participant G as GitHub job
    participant O as GitHub OIDC
    participant S as AWS STS
    participant R as IAM Role
    participant A as AWS Resource

    G->>O: request ID token
    O-->>G: signed token
    G->>S: AssumeRoleWithWebIdentity
    S->>R: evaluate trust conditions
    R-->>S: allow
    S-->>G: temporary credentials
    G->>A: deploy

C.2 AWS trust principles

The role trust should constrain:

  • issuer;
  • audience;
  • repository;
  • branch or GitHub environment;
  • immutable subject format if applicable to your repository.

The permission policy should constrain the AWS resources/actions separately.

C.3 Workflow

name: Deploy Production

on:
  workflow_dispatch:
    inputs:
      release:
        type: string
        required: true

permissions:
  contents: read

jobs:
  deploy:
    environment: production

    concurrency:
      group: production
      queue: max

    permissions:
      contents: read
      id-token: write

    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - uses: actions/checkout@v6

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@<PINNED_SHA>
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-prod-deploy
          aws-region: ap-northeast-1

      - name: Verify identity
        run: aws sts get-caller-identity

      - name: Deploy exact release
        env:
          RELEASE: ${{ inputs.release }}
        run: ./deploy-release.sh "$RELEASE"

Lab D — Build and Deploy a Container to Kubernetes

D.1 Requirements

  • build once;
  • push to registry;
  • deploy by digest;
  • production environment approval;
  • rollout verification;
  • smoke test.

D.2 Flow

flowchart LR
    C[Commit/tag] --> B[Build image]
    B --> R[(Registry)]
    R --> I[Image digest]
    I --> G{Production gate}
    G --> H[Helm deploy]
    H --> V[Rollout verify]
    V --> S[Smoke test]

D.3 Skeleton workflow

jobs:
  image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    outputs:
      digest: ${{ steps.build.outputs.digest }}

    steps:
      - uses: actions/checkout@v6

      - uses: docker/setup-buildx-action@<PINNED_SHA>

      - uses: docker/login-action@<PINNED_SHA>
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - id: build
        uses: docker/build-push-action@<PINNED_SHA>
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

  deploy:
    needs: image
    environment: production
    concurrency:
      group: production
      queue: max
    runs-on: self-hosted
    timeout-minutes: 20

    steps:
      - uses: actions/checkout@v6

      - name: Deploy immutable digest
        env:
          DIGEST: ${{ needs.image.outputs.digest }}
        run: |
          helm upgrade --install app ./chart \
            --namespace production \
            --set image.digest="$DIGEST" \
            --wait \
            --timeout 5m

      - name: Smoke test
        run: curl --fail --retry 5 https://app.example.com/health

A private Kubernetes API is a networking requirement; the self-hosted runner must be intentionally placed and secured for that trust zone.


Lab E — Terraform Plan / Approval / Apply

E.1 Safe model

flowchart LR
    PR[Pull request] --> P[Plan]
    P --> R[Review]
    R --> M[Merge]
    M --> A[Trusted apply workflow]
    A --> E[Protected environment]
    E --> C[Cloud]

Do not blindly reuse a plan produced by untrusted code in a privileged apply job without validating the exact source revision and trust model.

E.2 Plan job

name: Terraform Plan

on:
  pull_request:
    paths: ["infra/**"]

permissions:
  contents: read
  id-token: write

jobs:
  plan:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: infra

    steps:
      - uses: actions/checkout@v6
      - run: terraform fmt -check -recursive
      - run: terraform init -input=false
      - run: terraform validate
      - run: terraform plan -input=false

E.3 Apply job

name: Terraform Apply

on:
  push:
    branches: [main]
    paths: ["infra/**"]

permissions:
  contents: read

jobs:
  apply:
    environment: production

    permissions:
      contents: read
      id-token: write

    concurrency:
      group: terraform-production
      queue: max

    runs-on: self-hosted
    timeout-minutes: 30

    defaults:
      run:
        working-directory: infra

    steps:
      - uses: actions/checkout@v6
      - run: terraform init -input=false
      - run: terraform plan -input=false -out=tfplan
      - run: terraform apply -input=false tfplan

In a mature setup, policy/security checks and plan review happen before apply.


Lab F — ARC Runner Fleet for Private Infrastructure

F.1 Use case

The organization must deploy to:

  • private Kubernetes API;
  • internal artifact registry;
  • private database migration endpoint.

GitHub-hosted runners cannot directly reach the resources.

F.2 Architecture

flowchart TB
    G[GitHub Actions] <--> L[ARC listener]
    L --> C[ARC controller]

    subgraph VPC["Private VPC / Kubernetes"]
      C --> P[Ephemeral runner pods]
      P --> E[EKS private endpoint]
      P --> R[Private registry]
      P --> D[Internal services]
    end

F.3 Trust zoning

Use separate scale sets:

arc-general
arc-infra
arc-production

arc-production:

  • production repos only;
  • no public PRs;
  • dedicated Kubernetes namespace;
  • restricted service account;
  • production network access only as required;
  • ephemeral runners;
  • audited changes.

Lab G — Debugging a Workflow Systematically

Suppose a production deployment job is not starting.

Use this sequence:

flowchart TD
    A[Workflow exists?] --> B[Trigger matched?]
    B --> C[Job if true?]
    C --> D[needs succeeded?]
    D --> E[Environment approved?]
    E --> F[Runner available?]
    F --> G[Runner labels/group correct?]
    G --> H[Job starts]
    H --> I[Auth works?]
    I --> J[Network works?]
    J --> K[Deploy works?]

Do not start by randomly changing IAM and firewall rules. Identify the layer first.


Part IV — Decision Guides and Cheat Sheets

Decision Guide — Which Reuse Mechanism?

RequirementYAML anchorComposite actionReusable workflowWorkflow template
Reuse static YAMLExcellentNoNoInitial only
Reuse stepsLimitedExcellentGoodInitial only
Reuse jobsNoNoExcellentInitial only
Choose runnerNoNoYesYes
Central updates affect callersNoYes by versionYes by versionNo
Simple repository bootstrapNoNoGoodExcellent
Cross-repository standardLimitedYesExcellentExcellent

Decision Guide — Runner Choice

flowchart TD
    A[Need a runner] --> B{Private network required?}
    B -- No --> C{Special CPU/RAM/GPU/static IP?}
    C -- No --> H[Standard GitHub-hosted]
    C -- Yes --> L[Larger GitHub-hosted]
    B -- Yes --> D{Can supported hosted private networking meet need?}
    D -- Yes --> L
    D -- No --> E{Kubernetes platform available?}
    E -- Yes --> R[ARC ephemeral runners]
    E -- No --> S[Ephemeral self-hosted VM runners]

Decision Guide — Cache or Artifact?

flowchart TD
    A[Need to persist data?] --> B{Is it required for correctness / promotion?}
    B -- Yes --> C[Artifact / package / registry]
    B -- No --> D{Is it reusable performance data?}
    D -- Yes --> E[Cache]
    D -- No --> F[Do not persist]

Decision Guide — pull_request or pull_request_target?

NeedEvent
Build/test PR codepull_request
Test fork code safelypull_request
Label/comment using base-repo privileges without running PR codepull_request_target
Privileged follow-up after untrusted CIoften workflow_run
Execute fork code with production secretDo not design this

Decision Guide — Secret or Variable?

ValueUse
API hostnamevariable
regionvariable
feature togglevariable
passwordsecret
private keysecret
webhook bearer tokensecret
cloud access keypreferably replace with OIDC
environment nameinput/variable

Cheat Sheet — Minimal Secure PR CI

name: PR CI

on:
  pull_request:

permissions:
  contents: read

cache-mode: read

concurrency:
  group: pr-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - uses: actions/checkout@v6
      - run: ./ci.sh

Cheat Sheet — Minimal Protected Production Job

jobs:
  deploy:
    environment: production

    permissions:
      contents: read
      id-token: write

    concurrency:
      group: production
      queue: max

    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - run: ./deploy.sh

Cheat Sheet — Data Passing

same command              -> shell variable
later step, same job      -> GITHUB_ENV
small computed value      -> GITHUB_OUTPUT
dependent job             -> job output + needs
whole file/directory      -> artifact
later workflow/run        -> package/artifact/external store
speed optimization        -> cache

Cheat Sheet — Troubleshooting

SymptomFirst checks
Workflow never startsevent, branch, path, workflow location
Job skippedif, needs, environment
Job queued foreverrunner labels/group/capacity
403 GitHub APIpermissions, fork policy
Cloud auth failsOIDC permission, audience, subject, role trust
Private endpoint failsrunner network/DNS/firewall
Cache misseskey/hash/scope/cache-mode
Service unavailablehealth check/port/hostname/startup
Artifact missingpath/name/retention/permissions
Works locally, fails in Actionsshell, OS, env, file case, clean checkout

Part V — Official Reference Links

Use official GitHub documentation as the source of truth for version-sensitive syntax and limits:


Final Production Checklist

Before calling a GitHub Actions platform “production ready”:

Workflow correctness

  • [ ] Triggers are narrow and intentional.
  • [ ] Job dependencies match the desired DAG.
  • [ ] Timeouts exist.
  • [ ] Concurrency exists where races are unsafe.
  • [ ] Dynamic data uses outputs/artifacts appropriately.
  • [ ] CI can succeed without cache.

Security

  • [ ] GITHUB_TOKEN is least privilege.
  • [ ] Untrusted event data is not interpolated into executable shell syntax.
  • [ ] pull_request_target is not executing untrusted PR code.
  • [ ] Third-party actions are reviewed and SHA-pinned.
  • [ ] Fork PRs do not reach privileged self-hosted runners.
  • [ ] Production cloud auth uses OIDC where possible.
  • [ ] Production environment is protected.
  • [ ] Long-lived secrets are inventoried and rotated.
  • [ ] Cache trust boundaries are explicit.

Runners

  • [ ] Runner type is appropriate.
  • [ ] Self-hosted runners are patched.
  • [ ] Sensitive pools are ephemeral.
  • [ ] Runner groups restrict repositories.
  • [ ] Private network paths are explicit.
  • [ ] No unnecessary ambient cloud identity.
  • [ ] Capacity/queue metrics exist.

Supply chain

  • [ ] Build is reproducible enough for the release model.
  • [ ] Release artifacts are immutable.
  • [ ] Build once/deploy many is used where practical.
  • [ ] Image/package provenance is available where needed.
  • [ ] Artifact retention is intentional.
  • [ ] Deployment uses exact artifact/image identity.

Governance

  • [ ] Required checks enforced.
  • [ ] Merge queue workflows support merge_group if merge queue is used.
  • [ ] Actions policy restricts unreviewed code.
  • [ ] CODEOWNERS/review policy is configured.
  • [ ] Production deployment approvals/protection are configured.
  • [ ] Audit and ownership are documented.

Operations

  • [ ] Workflow duration/queue/failure metrics are visible.
  • [ ] Cost is measured.
  • [ ] Common failures have runbooks.
  • [ ] Rollback is tested.
  • [ ] Central reusable workflows have staged rollout/testing.
  • [ ] Action/runner/runtime upgrades have an owner.

Closing Mental Model

If you remember only one architecture, remember this:

flowchart LR
    E[Event] --> W[Workflow]
    W --> P[Policy + permissions]
    P --> J[Jobs]
    J --> R[Runners]
    R --> S[Steps/actions]
    S --> A[Immutable artifacts]
    A --> G[Environment gates]
    G --> D[Deployment]
    D --> V[Verification]
    V --> O[Observability + audit]

A production GitHub Actions system is not “some YAML that runs tests.”

It is an event-driven execution platform with:

  • identity;
  • policy;
  • compute;
  • data flow;
  • supply-chain trust;
  • deployment controls;
  • observability;
  • governance.

Design all eight deliberately.

Appendix A — 2026-Sensitive Features Worth Rechecking

The core GitHub Actions model is stable, but several capabilities in this handbook are newer or especially version-sensitive. Before standardizing them across an enterprise, confirm the current GitHub documentation for your GitHub.com/GHES version.

FeatureCurrent handbook behavior
actions/checkoutExamples use @v6
actions/setup-nodeExamples use @v7
Cache accesscache-mode: read/write/write-only/none
Concurrency queuesqueue: max can retain multiple pending runs
Step concurrencybackground, wait, wait-all, cancel, and parallel
Environment without deployment objectenvironment.deployment: false
Same-repository action reference$/.github/actions/... on supported GitHub.com workflows
Reusable workflowsCurrent reference documents 10 nesting levels and 50 unique called workflows
OIDC subjectNewer/opted-in repositories can use immutable owner/repository-ID subject formats
Hosted imagesCurrent GitHub-hosted runner catalog includes newer Ubuntu/Windows/macOS/ARM images

Treat these as capabilities to verify, not assumptions to copy from an older installation.

Appendix B — Complete Source Topic Coverage Audit

This appendix preserves the exact curriculum vocabulary from the supplied topic map so the handbook is searchable and auditable against the requested scope. The explanations, examples, diagrams, and production guidance live in Chapters 1–62; this appendix is a traceability checklist, not a second tutorial.

1. BASIC — GitHub Actions Foundations

1.1 Introduction to GitHub Actions

  • [x] GitHub Actions overview
  • [x] CI
  • [x] Continuous Delivery
  • [x] Continuous Deployment
  • [x] Automation
  • [x] Event-driven automation
  • [x] Repository automation
  • [x] DevOps automation
  • [x] GitHub Actions use cases
  • [x] GitHub Actions terminology
  • [x] GitHub Actions architecture
  • [x] GitHub Actions execution model
  • [x] Workflow lifecycle
  • [x] Workflow run lifecycle

1.2 Core Components

  • [x] Workflows
  • [x] Workflow runs
  • [x] Events
  • [x] Triggers
  • [x] Jobs
  • [x] Steps
  • [x] Actions
  • [x] Runners
  • [x] Commands
  • [x] Scripts
  • [x] Artifacts
  • [x] Caches
  • [x] Variables
  • [x] Secrets
  • [x] Contexts
  • [x] Expressions
  • [x] Environments

1.3 Workflow Files

  • [x] .github/workflows
  • [x] .yml
  • [x] .yaml
  • [x] Multiple workflows
  • [x] Workflow naming
  • [x] Workflow file naming
  • [x] Workflow version control
  • [x] Workflow visibility
  • [x] Workflow enablement
  • [x] Workflow disablement

1.4 Basic Workflow Structure

  • [x] name
  • [x] run-name
  • [x] on
  • [x] permissions
  • [x] env
  • [x] defaults
  • [x] concurrency
  • [x] jobs
  • [x] runs-on
  • [x] steps
  • [x] uses
  • [x] run
  • [x] with

2. FUNDAMENTALS — Workflow Syntax and Execution

2.1 YAML Fundamentals

  • [x] YAML indentation
  • [x] YAML mappings
  • [x] YAML sequences
  • [x] YAML strings
  • [x] YAML booleans
  • [x] YAML multiline strings
  • [x] YAML quoting
  • [x] YAML comments
  • [x] YAML anchors
  • [x] YAML aliases
  • [x] YAML reusable structures
  • [x] YAML syntax validation

2.2 Workflow-Level Syntax

  • [x] name
  • [x] run-name
  • [x] on
  • [x] permissions
  • [x] env
  • [x] defaults
  • [x] defaults.run
  • [x] defaults.run.shell
  • [x] defaults.run.working-directory
  • [x] concurrency
  • [x] jobs
  • [x] cache-mode

2.3 Job-Level Syntax

  • [x] jobs.<job_id>
  • [x] Job IDs
  • [x] name
  • [x] permissions
  • [x] needs
  • [x] if
  • [x] runs-on
  • [x] environment
  • [x] concurrency
  • [x] outputs
  • [x] env
  • [x] defaults
  • [x] timeout-minutes
  • [x] continue-on-error
  • [x] container
  • [x] services
  • [x] strategy
  • [x] uses
  • [x] with
  • [x] secrets
  • [x] cache-mode

2.4 Step-Level Syntax

  • [x] steps
  • [x] Step IDs
  • [x] id
  • [x] name
  • [x] if
  • [x] uses
  • [x] run
  • [x] shell
  • [x] with
  • [x] env
  • [x] continue-on-error
  • [x] timeout-minutes
  • [x] working-directory

3. FUNDAMENTALS — Events and Workflow Triggers

3.1 Trigger Concepts

  • [x] GitHub events
  • [x] Webhook events
  • [x] Activity types
  • [x] Manual triggers
  • [x] Scheduled triggers
  • [x] External triggers
  • [x] Multiple triggers
  • [x] Trigger filters
  • [x] Event payloads
  • [x] github.event
  • [x] GITHUB_EVENT_NAME
  • [x] GITHUB_EVENT_PATH

3.2 GitHub Actions Events

  • [x] branch_protection_rule
  • [x] check_run
  • [x] check_suite
  • [x] create
  • [x] delete
  • [x] deployment
  • [x] deployment_status
  • [x] discussion
  • [x] discussion_comment
  • [x] fork
  • [x] gollum
  • [x] image_version
  • [x] issue_comment
  • [x] issues
  • [x] label
  • [x] merge_group
  • [x] milestone
  • [x] page_build
  • [x] public
  • [x] pull_request
  • [x] pull_request_review
  • [x] pull_request_review_comment
  • [x] pull_request_target
  • [x] push
  • [x] registry_package
  • [x] release
  • [x] repository_dispatch
  • [x] schedule
  • [x] status
  • [x] watch
  • [x] workflow_call
  • [x] workflow_dispatch
  • [x] workflow_run

3.3 Trigger Filters

  • [x] types
  • [x] branches
  • [x] branches-ignore
  • [x] tags
  • [x] tags-ignore
  • [x] paths
  • [x] paths-ignore
  • [x] Glob patterns
  • [x] Negative patterns
  • [x] Branch filtering
  • [x] Tag filtering
  • [x] Path filtering
  • [x] Activity filtering
  • [x] Combined filters

3.4 Manual Workflows

  • [x] workflow_dispatch
  • [x] Manual workflow execution
  • [x] Workflow inputs
  • [x] Boolean inputs
  • [x] Choice inputs
  • [x] String inputs
  • [x] Number inputs
  • [x] Environment inputs
  • [x] Default input values
  • [x] Required inputs

3.5 Scheduled Workflows

  • [x] schedule
  • [x] Cron syntax
  • [x] Scheduled workflow limitations
  • [x] Default branch execution

3.6 External Workflow Triggers

  • [x] repository_dispatch
  • [x] client_payload
  • [x] REST API triggering
  • [x] GitHub CLI triggering
  • [x] Cross-repository triggers

4. FUNDAMENTALS — Jobs

4.1 Job Architecture

  • [x] Job definition
  • [x] Job isolation
  • [x] Sequential jobs
  • [x] Parallel jobs
  • [x] Job dependencies
  • [x] Job execution status
  • [x] Job conclusions
  • [x] Job failure
  • [x] Job cancellation
  • [x] Job skipping

4.2 Job Dependencies

  • [x] needs
  • [x] Dependency chains
  • [x] Fan-out jobs
  • [x] Fan-in jobs
  • [x] Dependency outputs
  • [x] Conditional dependent jobs

4.3 Job Conditions

  • [x] Job-level if
  • [x] success()
  • [x] failure()
  • [x] cancelled()
  • [x] always()
  • [x] Conditional execution

4.4 Job Outputs

  • [x] Job outputs
  • [x] Step-to-job outputs
  • [x] Job-to-job outputs
  • [x] needs context
  • [x] Output expressions

5. FUNDAMENTALS — Steps

5.1 Step Execution

  • [x] Action steps
  • [x] Shell steps
  • [x] Sequential step execution
  • [x] Step IDs
  • [x] Step names
  • [x] Step conditions
  • [x] Step outputs
  • [x] Step environment
  • [x] Step failure
  • [x] Step timeout
  • [x] Continue-on-error

5.2 Shell Commands

  • [x] Bash
  • [x] sh
  • [x] PowerShell
  • [x] pwsh
  • [x] Windows Command Prompt
  • [x] Python
  • [x] Custom shells
  • [x] Shell exit codes
  • [x] Multiline commands
  • [x] Working directories

6. ESSENTIALS — Runners

6.1 Runner Fundamentals

  • [x] Runner architecture
  • [x] Runner lifecycle
  • [x] Runner assignment
  • [x] Runner labels
  • [x] Runner groups
  • [x] Runner environment
  • [x] Runner workspace
  • [x] Runner temporary directory
  • [x] Runner tool cache

6.2 GitHub-Hosted Runners

  • [x] Ubuntu runners
  • [x] Windows runners
  • [x] macOS runners
  • [x] ARM runners
  • [x] Runner images
  • [x] Preinstalled software
  • [x] Runner image updates
  • [x] Hardware resources
  • [x] Runner filesystem
  • [x] Hosted runner networking

6.3 Larger Runners

  • [x] Larger GitHub-hosted runners
  • [x] Runner sizes
  • [x] Runner groups
  • [x] Static IP addresses
  • [x] Private networking
  • [x] Autoscaling
  • [x] GPU runners
  • [x] Larger-runner access control

6.4 Self-Hosted Runners

  • [x] Self-hosted runner architecture
  • [x] Repository-level runners
  • [x] Organization-level runners
  • [x] Enterprise-level runners
  • [x] Runner installation
  • [x] Runner registration
  • [x] Runner removal
  • [x] Runner labels
  • [x] Custom labels
  • [x] Runner groups
  • [x] Runner services
  • [x] Runner updates
  • [x] Runner versioning
  • [x] Runner networking
  • [x] Proxy configuration
  • [x] Firewall requirements
  • [x] Runner security
  • [x] Persistent runners
  • [x] Ephemeral runners
  • [x] Just-in-time runners
  • [x] Runner autoscaling
  • [x] Runner routing
  • [x] Runner monitoring
  • [x] Runner cleanup

6.5 Private Networking

  • [x] Private networking for hosted runners
  • [x] VNet/VPC connectivity
  • [x] Private resources
  • [x] Private package registries
  • [x] Private cloud endpoints
  • [x] Network allowlists
  • [x] DNS considerations

7. ESSENTIALS — Variables and Environment Variables

7.1 Environment Variables

  • [x] Workflow-level environment variables
  • [x] Job-level environment variables
  • [x] Step-level environment variables
  • [x] Environment variable precedence
  • [x] Default environment variables
  • [x] Custom environment variables

7.2 Configuration Variables

  • [x] Repository variables
  • [x] Organization variables
  • [x] Environment variables
  • [x] Variable scopes
  • [x] Variable precedence
  • [x] Variable naming
  • [x] Variable limits
  • [x] vars context

7.3 GitHub Default Variables

  • [x] CI
  • [x] GITHUB_ACTION
  • [x] GITHUB_ACTION_PATH
  • [x] GITHUB_ACTION_REPOSITORY
  • [x] GITHUB_ACTIONS
  • [x] GITHUB_ACTOR
  • [x] GITHUB_ACTOR_ID
  • [x] GITHUB_API_URL
  • [x] GITHUB_BASE_REF
  • [x] GITHUB_ENV
  • [x] GITHUB_EVENT_NAME
  • [x] GITHUB_EVENT_PATH
  • [x] GITHUB_GRAPHQL_URL
  • [x] GITHUB_HEAD_REF
  • [x] GITHUB_JOB
  • [x] GITHUB_OUTPUT
  • [x] GITHUB_PATH
  • [x] GITHUB_REF
  • [x] GITHUB_REF_NAME
  • [x] GITHUB_REF_PROTECTED
  • [x] GITHUB_REF_TYPE
  • [x] GITHUB_REPOSITORY
  • [x] GITHUB_REPOSITORY_ID
  • [x] GITHUB_REPOSITORY_OWNER
  • [x] GITHUB_RETENTION_DAYS
  • [x] GITHUB_RUN_ATTEMPT
  • [x] GITHUB_RUN_ID
  • [x] GITHUB_RUN_NUMBER
  • [x] GITHUB_SERVER_URL
  • [x] GITHUB_SHA
  • [x] GITHUB_STEP_SUMMARY
  • [x] GITHUB_TRIGGERING_ACTOR
  • [x] GITHUB_WORKFLOW
  • [x] GITHUB_WORKFLOW_REF
  • [x] GITHUB_WORKFLOW_SHA
  • [x] GITHUB_WORKSPACE
  • [x] RUNNER_ARCH
  • [x] RUNNER_ENVIRONMENT
  • [x] RUNNER_NAME
  • [x] RUNNER_OS
  • [x] RUNNER_TEMP
  • [x] RUNNER_TOOL_CACHE

8. ESSENTIALS — Contexts

  • [x] github
  • [x] env
  • [x] vars
  • [x] job
  • [x] jobs
  • [x] steps
  • [x] runner
  • [x] secrets
  • [x] strategy
  • [x] matrix
  • [x] needs
  • [x] inputs
  • [x] Context availability
  • [x] Context property access
  • [x] Property dereference syntax
  • [x] Index syntax
  • [x] Context security
  • [x] Context debugging
  • [x] Context dumping
  • [x] Context availability by workflow key

9. ESSENTIALS — Expressions

9.1 Expression Syntax

  • [x] ${{ }}
  • [x] Literals
  • [x] Booleans
  • [x] Null
  • [x] Numbers
  • [x] Strings
  • [x] Operators
  • [x] Operator precedence
  • [x] Property access
  • [x] Object filters
  • [x] Loose equality
  • [x] Type coercion

9.2 Expression Operators

  • [x] !
  • [x] >
  • [x] >=
  • [x] <
  • [x] <=
  • [x] ==
  • [x] !=
  • [x] &&
  • [x] ||

9.3 Expression Functions

  • [x] contains()
  • [x] startsWith()
  • [x] endsWith()
  • [x] format()
  • [x] join()
  • [x] toJSON()
  • [x] fromJSON()
  • [x] hashFiles()

9.4 Status Functions

  • [x] success()
  • [x] always()
  • [x] cancelled()
  • [x] failure()

10. ESSENTIALS — Secrets

  • [x] Repository secrets
  • [x] Organization secrets
  • [x] Environment secrets
  • [x] Secret scopes
  • [x] Secret precedence
  • [x] Secret naming
  • [x] Secret limits
  • [x] Secret access policies
  • [x] Secret masking
  • [x] Secret redaction
  • [x] Secret inheritance
  • [x] Secrets in reusable workflows
  • [x] Secrets in forked pull requests
  • [x] Secrets with Dependabot
  • [x] Secrets with command-line tools
  • [x] Large secrets
  • [x] Structured secrets
  • [x] Secret rotation
  • [x] Least-privilege secrets
  • [x] secrets context
  • [x] GITHUB_TOKEN

11. ESSENTIALS — GITHUB_TOKEN

  • [x] Automatic token generation
  • [x] Token lifecycle
  • [x] Token scope
  • [x] Token expiration
  • [x] Repository access
  • [x] permissions
  • [x] Workflow-level permissions
  • [x] Job-level permissions
  • [x] Read permissions
  • [x] Write permissions
  • [x] contents
  • [x] actions
  • [x] attestations
  • [x] checks
  • [x] deployments
  • [x] discussions
  • [x] id-token
  • [x] issues
  • [x] models
  • [x] packages
  • [x] pages
  • [x] pull-requests
  • [x] security-events
  • [x] statuses
  • [x] Least privilege
  • [x] Token-triggered workflow behavior
  • [x] GitHub API authentication
  • [x] GitHub CLI authentication

12. ESSENTIALS — Workflow Commands

  • [x] Workflow command syntax
  • [x] Environment files
  • [x] GITHUB_ENV
  • [x] GITHUB_OUTPUT
  • [x] GITHUB_PATH
  • [x] GITHUB_STEP_SUMMARY
  • [x] Setting environment variables
  • [x] Setting outputs
  • [x] Adding system paths
  • [x] Job summaries
  • [x] Error annotations
  • [x] Warning annotations
  • [x] Notice annotations
  • [x] Debug messages
  • [x] Log grouping
  • [x] group
  • [x] endgroup
  • [x] Secret masking
  • [x] add-mask
  • [x] Stopping workflow commands
  • [x] Action state
  • [x] GITHUB_STATE

13. ESSENTIALS — Matrix Builds

  • [x] Matrix strategy
  • [x] strategy.matrix
  • [x] Multi-dimensional matrices
  • [x] Operating-system matrices
  • [x] Runtime-version matrices
  • [x] Include
  • [x] Exclude
  • [x] Matrix expansion
  • [x] Dynamic matrices
  • [x] Matrix from JSON
  • [x] Matrix outputs
  • [x] Matrix job dependencies
  • [x] max-parallel
  • [x] fail-fast
  • [x] Matrix continue-on-error
  • [x] Experimental matrix jobs
  • [x] Matrix limits

14. ESSENTIALS — Caching

  • [x] Dependency caching
  • [x] actions/cache
  • [x] Automatic dependency caching
  • [x] Cache keys
  • [x] Restore keys
  • [x] Cache matching
  • [x] Cache scope
  • [x] Cache version
  • [x] Cache hits
  • [x] Cache misses
  • [x] Cross-OS cache
  • [x] Cache eviction
  • [x] Cache retention
  • [x] Cache limits
  • [x] Cache security
  • [x] Cache poisoning
  • [x] Cache access restrictions
  • [x] cache-mode
  • [x] read
  • [x] write
  • [x] write-only
  • [x] none
  • [x] Trigger-sensitive cache permissions
  • [x] Package-manager caches
  • [x] npm caching
  • [x] Yarn caching
  • [x] pnpm caching
  • [x] Maven caching
  • [x] Gradle caching
  • [x] pip caching
  • [x] Bundler caching
  • [x] NuGet caching

15. ESSENTIALS — Artifacts

  • [x] Workflow artifacts
  • [x] Artifact upload
  • [x] Artifact download
  • [x] Artifact naming
  • [x] Artifact paths
  • [x] Artifact compression
  • [x] Artifact retention
  • [x] Artifact expiration
  • [x] Artifact IDs
  • [x] Artifact URLs
  • [x] Artifact digests
  • [x] Artifact sharing between jobs
  • [x] Artifact sharing between workflow runs
  • [x] Artifact permissions
  • [x] Artifact deletion
  • [x] Artifact API
  • [x] Cache vs artifacts
  • [x] Build artifacts
  • [x] Test artifacts
  • [x] Log artifacts
  • [x] Deployment artifacts
  • [x] Artifact attestations

16. ESSENTIALS — Concurrency

  • [x] Workflow concurrency
  • [x] Job concurrency
  • [x] Concurrency groups
  • [x] Dynamic concurrency groups
  • [x] cancel-in-progress
  • [x] Pending runs
  • [x] Running runs
  • [x] Deployment serialization
  • [x] Branch-specific concurrency
  • [x] Environment-specific concurrency
  • [x] Workflow cancellation

17. ESSENTIALS — Containers

17.1 Container Jobs

  • [x] container
  • [x] Container images
  • [x] Container credentials
  • [x] Container environment variables
  • [x] Container ports
  • [x] Container volumes
  • [x] Container options
  • [x] Docker Hub
  • [x] GitHub Container Registry
  • [x] Private container registries

17.2 Service Containers

  • [x] services
  • [x] Database services
  • [x] Redis
  • [x] PostgreSQL
  • [x] MySQL
  • [x] MongoDB
  • [x] Service networking
  • [x] Port mappings
  • [x] Health checks
  • [x] Service credentials
  • [x] Container-to-container communication

18. ESSENTIALS — Using Actions

  • [x] GitHub Marketplace
  • [x] Official GitHub actions
  • [x] Community actions
  • [x] Public actions
  • [x] Private actions
  • [x] Local repository actions
  • [x] Referencing actions
  • [x] Action inputs
  • [x] Action outputs
  • [x] Action versions
  • [x] Tags
  • [x] Branch references
  • [x] Commit SHA pinning
  • [x] Semantic versioning
  • [x] Action updates
  • [x] Action dependency management
  • [x] Action trust
  • [x] Third-party action review
  • [x] Deprecated actions
  • [x] Node runtime migrations

19. ESSENTIALS — Reusable Automation

19.1 Reusable Workflows

  • [x] workflow_call
  • [x] Called workflows
  • [x] Caller workflows
  • [x] Reusable workflow inputs
  • [x] Reusable workflow secrets
  • [x] secrets: inherit
  • [x] Reusable workflow outputs
  • [x] Job outputs
  • [x] Nested reusable workflows
  • [x] Reusable workflow permissions
  • [x] Reusable workflow environments
  • [x] Reusable workflow matrices
  • [x] Cross-repository reusable workflows
  • [x] Private reusable workflows
  • [x] Workflow versioning
  • [x] Workflow pinning
  • [x] Reusable workflow access rules
  • [x] Reusable workflow limitations

19.2 Workflow Templates

  • [x] Organization workflow templates
  • [x] Starter workflows
  • [x] Workflow template repositories
  • [x] Template metadata
  • [x] Organization CI standards

19.3 YAML Reuse

  • [x] YAML anchors
  • [x] YAML aliases

20. ESSENTIALS — CI Pipelines

  • [x] Checkout
  • [x] Dependency installation
  • [x] Dependency caching
  • [x] Compilation
  • [x] Linting
  • [x] Formatting
  • [x] Unit testing
  • [x] Integration testing
  • [x] Functional testing
  • [x] End-to-end testing
  • [x] Security testing
  • [x] Code coverage
  • [x] Test reports
  • [x] Build packaging
  • [x] Artifact publishing
  • [x] Pipeline status checks
  • [x] Pull-request validation
  • [x] Merge validation
  • [x] Branch builds
  • [x] Tag builds
  • [x] Release builds

21. ESSENTIALS — CD Pipelines

  • [x] Continuous delivery
  • [x] Continuous deployment
  • [x] Deployment jobs
  • [x] Deployment workflows
  • [x] Deployment environments
  • [x] Development deployments
  • [x] Staging deployments
  • [x] UAT deployments
  • [x] Production deployments
  • [x] Manual approvals
  • [x] Deployment gates
  • [x] Deployment promotion
  • [x] Deployment rollback
  • [x] Deployment verification
  • [x] Post-deployment testing
  • [x] Deployment status
  • [x] Deployment history

22. ESSENTIALS — Environments and Deployments

  • [x] GitHub environments
  • [x] Environment names
  • [x] Environment URLs
  • [x] Environment secrets
  • [x] Environment variables
  • [x] Required reviewers
  • [x] Deployment protection rules
  • [x] Custom deployment protection rules
  • [x] Wait timers
  • [x] Branch restrictions
  • [x] Tag restrictions
  • [x] Environment approvals
  • [x] Environment deployments
  • [x] Environment concurrency
  • [x] Deployment history
  • [x] Deployment API

23. ADVANCED — Custom Actions

23.1 Custom Action Fundamentals

  • [x] Custom action architecture
  • [x] Action repositories
  • [x] Action metadata
  • [x] action.yml
  • [x] action.yaml
  • [x] Inputs
  • [x] Outputs
  • [x] Environment variables
  • [x] Action branding
  • [x] Action versioning
  • [x] Action publishing

23.2 JavaScript Actions

  • [x] JavaScript actions
  • [x] Node.js runtime
  • [x] @actions/core
  • [x] @actions/github
  • [x] @actions/exec
  • [x] @actions/io
  • [x] @actions/tool-cache
  • [x] GitHub REST API
  • [x] Octokit
  • [x] Action inputs
  • [x] Action outputs
  • [x] Action failures
  • [x] Action logging
  • [x] Pre-actions
  • [x] Post-actions
  • [x] State management
  • [x] Packaging dependencies
  • [x] ncc
  • [x] JavaScript action releases

23.3 Docker Container Actions

  • [x] Docker actions
  • [x] Dockerfile
  • [x] Docker action metadata
  • [x] Docker action inputs
  • [x] Docker action arguments
  • [x] Docker entrypoints
  • [x] Container environment
  • [x] Container filesystem
  • [x] Container exit codes
  • [x] Docker action publishing

23.4 Composite Actions

  • [x] Composite actions
  • [x] Composite steps
  • [x] Composite inputs
  • [x] Composite outputs
  • [x] Composite environment handling
  • [x] Nested actions
  • [x] Composite action limitations

23.5 Action Metadata Syntax

  • [x] name
  • [x] author
  • [x] description
  • [x] inputs
  • [x] outputs
  • [x] runs
  • [x] using
  • [x] main
  • [x] pre
  • [x] post
  • [x] pre-if
  • [x] post-if
  • [x] image
  • [x] entrypoint
  • [x] args
  • [x] Composite steps
  • [x] branding

24. ADVANCED — Workflow Data Flow

  • [x] Step outputs
  • [x] Job outputs
  • [x] Workflow outputs
  • [x] Reusable workflow outputs
  • [x] Environment files
  • [x] Context-based data passing
  • [x] JSON serialization
  • [x] toJSON
  • [x] fromJSON
  • [x] Artifact-based data transfer
  • [x] Output-based data transfer
  • [x] Matrix-generated data
  • [x] Dynamic workflow data
  • [x] State between pre/main/post actions

25. ADVANCED — Conditional Workflows

  • [x] Conditional jobs
  • [x] Conditional steps
  • [x] Event-based conditions
  • [x] Branch conditions
  • [x] Tag conditions
  • [x] Actor conditions
  • [x] Repository conditions
  • [x] Environment conditions
  • [x] Matrix conditions
  • [x] Changed-path conditions
  • [x] Pull-request conditions
  • [x] Fork conditions
  • [x] Failure handling
  • [x] Cleanup jobs
  • [x] Always-run jobs

26. ADVANCED — Error Handling

  • [x] Exit codes
  • [x] Step failures
  • [x] Job failures
  • [x] Workflow failures
  • [x] continue-on-error
  • [x] Matrix experimental failures
  • [x] fail-fast
  • [x] Retry patterns
  • [x] Conditional retry
  • [x] Cleanup on failure
  • [x] Rollback on failure
  • [x] success()
  • [x] failure()
  • [x] cancelled()
  • [x] always()

27. ADVANCED — Troubleshooting and Debugging

  • [x] Workflow syntax errors
  • [x] YAML errors
  • [x] Trigger debugging
  • [x] Context debugging
  • [x] Expression debugging
  • [x] Runner debugging
  • [x] Action debugging
  • [x] Container debugging
  • [x] Service-container debugging
  • [x] Permissions errors
  • [x] Authentication errors
  • [x] Secret errors
  • [x] Environment errors
  • [x] Cache errors
  • [x] Artifact errors
  • [x] Matrix errors
  • [x] Reusable workflow errors
  • [x] Deployment errors
  • [x] Timeout troubleshooting
  • [x] Network troubleshooting
  • [x] DNS troubleshooting
  • [x] Workflow logs
  • [x] Job logs
  • [x] Step logs
  • [x] Log search
  • [x] Log download
  • [x] Debug logging
  • [x] Runner diagnostic logging
  • [x] Re-running workflows
  • [x] Re-running failed jobs
  • [x] Re-running individual jobs
  • [x] Workflow run attempts
  • [x] Annotations
  • [x] Job summaries

28. ADVANCED — Security Hardening

28.1 Workflow Security

  • [x] Least privilege
  • [x] Workflow permissions
  • [x] Job permissions
  • [x] Untrusted input
  • [x] Script injection
  • [x] Command injection
  • [x] Expression injection
  • [x] Shell injection
  • [x] Pull-request security
  • [x] Fork security
  • [x] Public repository workflow security

28.2 Third-Party Action Security

  • [x] Action provenance
  • [x] Commit SHA pinning
  • [x] Mutable tags
  • [x] Action review
  • [x] Dependency review
  • [x] Supply-chain attacks
  • [x] Compromised actions
  • [x] Action allowlists

28.3 pull_request Security

  • [x] Fork pull requests
  • [x] Read-only tokens
  • [x] Secret restrictions
  • [x] Workflow approval
  • [x] Untrusted code execution

28.4 pull_request_target Security

  • [x] pull_request_target
  • [x] Privileged workflow context
  • [x] Untrusted checkout risks
  • [x] Secret exposure
  • [x] Token exposure
  • [x] Cache poisoning
  • [x] Secure pull_request_target patterns

28.5 Runner Security

  • [x] Hosted runner isolation
  • [x] Self-hosted runner risks
  • [x] Persistent runner risks
  • [x] Ephemeral runners
  • [x] Runner compromise
  • [x] Runner credential cleanup
  • [x] Network isolation
  • [x] Runner trust boundaries
  • [x] Public repository runner risks

29. ADVANCED — OpenID Connect

  • [x] OIDC
  • [x] Federated authentication
  • [x] Short-lived credentials
  • [x] ID tokens
  • [x] id-token: write
  • [x] OIDC token claims
  • [x] OIDC subject claims
  • [x] Custom subject claims
  • [x] Audience claims
  • [x] Repository claims
  • [x] Branch claims
  • [x] Tag claims
  • [x] Environment claims
  • [x] Cloud trust policies
  • [x] AWS OIDC
  • [x] Azure OIDC
  • [x] Google Cloud OIDC
  • [x] HashiCorp Vault OIDC
  • [x] Cloud role assumption
  • [x] Credential-less cloud deployments
  • [x] OIDC security

30. ADVANCED — Artifact Attestations

  • [x] Artifact provenance
  • [x] Artifact attestations
  • [x] Build provenance
  • [x] Attestation generation
  • [x] Attestation verification
  • [x] Signed provenance
  • [x] Software supply-chain integrity
  • [x] Container image attestations
  • [x] Binary attestations
  • [x] Package attestations
  • [x] Kubernetes attestation enforcement
  • [x] Kubernetes admission controller

31. ADVANCED — Docker and Container CI/CD

  • [x] Docker builds
  • [x] Docker Buildx
  • [x] BuildKit
  • [x] Multi-platform builds
  • [x] Docker layer caching
  • [x] Registry authentication
  • [x] GitHub Container Registry
  • [x] Container tagging
  • [x] Container metadata
  • [x] Image publishing
  • [x] Image signing
  • [x] Image attestations
  • [x] Image scanning
  • [x] Multi-architecture images
  • [x] Release images

32. ADVANCED — Package Publishing

  • [x] GitHub Packages
  • [x] npm packages
  • [x] Maven packages
  • [x] Gradle packages
  • [x] NuGet packages
  • [x] RubyGems
  • [x] Container packages
  • [x] Package authentication
  • [x] Package permissions
  • [x] Package publishing
  • [x] Package installation
  • [x] Package versioning
  • [x] Package provenance

33. ADVANCED — Releases

  • [x] Release workflows
  • [x] Git tags
  • [x] Release events
  • [x] GitHub Releases
  • [x] Release assets
  • [x] Release notes
  • [x] Automated releases
  • [x] Semantic versioning
  • [x] Pre-releases
  • [x] Draft releases
  • [x] Release artifact publishing
  • [x] Release promotion

34. ADVANCED — Cloud Deployments

34.1 AWS

  • [x] AWS authentication
  • [x] AWS OIDC
  • [x] IAM roles
  • [x] STS AssumeRole
  • [x] S3 deployments
  • [x] EC2 deployments
  • [x] ECS deployments
  • [x] ECR publishing
  • [x] EKS deployments
  • [x] Lambda deployments
  • [x] CloudFormation deployments
  • [x] Terraform deployments

34.2 Azure

  • [x] Azure authentication
  • [x] Azure OIDC
  • [x] Azure Web Apps
  • [x] Azure Container Apps
  • [x] AKS
  • [x] Azure Functions

34.3 Google Cloud

  • [x] Google Cloud authentication
  • [x] Workload Identity Federation
  • [x] GKE
  • [x] Cloud Run
  • [x] Artifact Registry

35. ADVANCED — Kubernetes CI/CD

  • [x] Kubernetes authentication
  • [x] kubeconfig management
  • [x] OIDC authentication
  • [x] Kubernetes manifests
  • [x] Helm
  • [x] Kustomize
  • [x] kubectl
  • [x] Namespace deployments
  • [x] Deployment rollout
  • [x] Deployment verification
  • [x] Rollback
  • [x] EKS
  • [x] AKS
  • [x] GKE
  • [x] Container registry integration
  • [x] GitOps integration

36. ADVANCED — Infrastructure as Code

  • [x] Terraform workflows
  • [x] Terraform formatting
  • [x] Terraform validation
  • [x] Terraform plan
  • [x] Terraform apply
  • [x] Terraform destroy
  • [x] Terraform state
  • [x] Terraform workspaces
  • [x] Terraform Cloud
  • [x] OpenTofu
  • [x] CloudFormation
  • [x] CDK
  • [x] Pulumi
  • [x] Infrastructure approvals
  • [x] Plan artifacts
  • [x] IaC security scanning
  • [x] Drift detection

37. ADVANCED — Self-Hosted Runner Architecture

  • [x] Runner fleet architecture
  • [x] Dedicated runners
  • [x] Shared runners
  • [x] Ephemeral runners
  • [x] Autoscaling runners
  • [x] JIT runners
  • [x] Runner groups
  • [x] Custom runner labels
  • [x] Repository runner isolation
  • [x] Organization runner isolation
  • [x] Enterprise runner isolation
  • [x] Runner images
  • [x] Golden runner images
  • [x] Runner patching
  • [x] Runner upgrades
  • [x] Runner network access
  • [x] Private subnet runners
  • [x] Proxy runners
  • [x] Runner observability
  • [x] Runner credential management
  • [x] Runner lifecycle automation

38. ADVANCED — Actions Runner Controller

  • [x] Actions Runner Controller
  • [x] ARC architecture
  • [x] Kubernetes-based runners
  • [x] Runner scale sets
  • [x] Autoscaling runner scale sets
  • [x] Scale-set controller
  • [x] Listener
  • [x] Runner pods
  • [x] Ephemeral runner pods
  • [x] ARC Helm charts
  • [x] GitHub App authentication
  • [x] PAT authentication
  • [x] Kubernetes RBAC
  • [x] Runner namespaces
  • [x] Custom runner images
  • [x] Docker-in-Docker
  • [x] Kubernetes container mode
  • [x] Runner storage
  • [x] Runner networking
  • [x] ARC autoscaling
  • [x] Minimum runners
  • [x] Maximum runners
  • [x] Runner groups
  • [x] ARC upgrades
  • [x] ARC monitoring
  • [x] ARC troubleshooting
  • [x] ARC security

39. ADVANCED — Workflow Performance

  • [x] Workflow runtime optimization
  • [x] Parallel jobs
  • [x] Matrix parallelism
  • [x] Dependency caching
  • [x] Build caching
  • [x] Docker caching
  • [x] Artifact optimization
  • [x] Checkout optimization
  • [x] Shallow clones
  • [x] Sparse checkout
  • [x] Dependency reuse
  • [x] Selective execution
  • [x] Path filtering
  • [x] Job dependency optimization
  • [x] Runner selection
  • [x] Larger runners
  • [x] Self-hosted runner performance

40. ADVANCED — Cost Optimization

  • [x] GitHub Actions minutes
  • [x] Storage usage
  • [x] Artifact storage
  • [x] Cache storage
  • [x] Hosted runner billing
  • [x] Larger runner billing
  • [x] Private repository usage
  • [x] Public repository usage
  • [x] Workflow runtime reduction
  • [x] Matrix cost control
  • [x] Retention optimization
  • [x] Self-hosted runner economics
  • [x] Usage monitoring
  • [x] Billing reports

41. ADVANCED — Monorepo Workflows

  • [x] Monorepo CI
  • [x] Path filters
  • [x] Selective builds
  • [x] Selective tests
  • [x] Component matrices
  • [x] Dynamic matrices
  • [x] Changed component detection
  • [x] Independent deployments
  • [x] Shared workflows
  • [x] Shared actions
  • [x] Dependency graph workflows
  • [x] Fan-out builds
  • [x] Fan-in validation
  • [x] Monorepo caching
  • [x] Monorepo concurrency

42. ADVANCED — Multi-Repository Workflows

  • [x] Cross-repository workflows
  • [x] repository_dispatch
  • [x] Reusable workflows across repositories
  • [x] Private reusable workflows
  • [x] Repository access policies
  • [x] Cross-repository deployment
  • [x] Central CI repositories
  • [x] Central workflow repositories
  • [x] Organization automation repositories
  • [x] GitHub App authentication
  • [x] Fine-grained PAT authentication
  • [x] Cross-repository artifacts
  • [x] Cross-repository orchestration

43. ADVANCED — Workflow Chaining

  • [x] workflow_run
  • [x] Workflow dependencies
  • [x] Upstream workflows
  • [x] Downstream workflows
  • [x] Multi-stage workflow chains
  • [x] Artifact passing between workflow runs
  • [x] Privilege boundaries
  • [x] Workflow chain security
  • [x] Workflow chain limits

44. ADVANCED — GitHub API Integration

  • [x] REST API
  • [x] GraphQL API
  • [x] GitHub CLI
  • [x] gh
  • [x] gh api
  • [x] Octokit
  • [x] actions/github-script
  • [x] Issues automation
  • [x] Pull-request automation
  • [x] Release automation
  • [x] Repository automation
  • [x] Deployment API
  • [x] Checks API
  • [x] Status API
  • [x] Actions API
  • [x] Workflow API
  • [x] Artifact API
  • [x] Cache API
  • [x] Runner API

45. ADVANCED — Repository Governance

  • [x] Branch protection
  • [x] Rulesets
  • [x] Required status checks
  • [x] Required workflows
  • [x] Merge queues
  • [x] merge_group
  • [x] CODEOWNERS interaction
  • [x] Pull-request approval
  • [x] Workflow approval
  • [x] Deployment approval
  • [x] Environment protection
  • [x] Repository Actions settings
  • [x] Workflow permissions

46. ADVANCED — Organization Governance

  • [x] Organization Actions policies
  • [x] Allowed actions
  • [x] Selected actions
  • [x] Marketplace action restrictions
  • [x] SHA pinning policies
  • [x] Reusable workflow governance
  • [x] Runner groups
  • [x] Organization secrets
  • [x] Organization variables
  • [x] Secret repository access
  • [x] Runner repository access
  • [x] Private action sharing
  • [x] Workflow retention
  • [x] Workflow permissions
  • [x] Fork workflow policies

47. ADVANCED — Enterprise Governance

  • [x] Enterprise Actions policies
  • [x] Enterprise runner groups
  • [x] Enterprise secrets
  • [x] Enterprise workflow policies
  • [x] Enterprise action restrictions
  • [x] Enterprise runner management
  • [x] Organization policy inheritance
  • [x] Actions usage policies
  • [x] Workflow execution policies
  • [x] Audit logs
  • [x] Enterprise security controls
  • [x] Enterprise billing
  • [x] Enterprise usage monitoring

48. ADVANCED — Workflow Execution Policies

  • [x] Workflow execution protections
  • [x] Workflow execution allowlists
  • [x] Actor restrictions
  • [x] Event restrictions
  • [x] Repository policies
  • [x] Organization policies
  • [x] Enterprise policies
  • [x] Policy inheritance
  • [x] Policy evaluation
  • [x] Workflow execution governance

49. ADVANCED — Metrics and Observability

  • [x] GitHub Actions metrics
  • [x] Workflow metrics
  • [x] Job metrics
  • [x] Workflow duration
  • [x] Queue duration
  • [x] Job duration
  • [x] Workflow success rate
  • [x] Workflow failure rate
  • [x] Runner utilization
  • [x] Runner availability
  • [x] Usage metrics
  • [x] Performance analysis
  • [x] Cost analysis
  • [x] Audit logs
  • [x] Workflow notifications

50. ADVANCED — Workflow Notifications

  • [x] Workflow run notifications
  • [x] Email notifications
  • [x] Web notifications
  • [x] Failed workflow notifications
  • [x] GitHub notifications
  • [x] Slack integrations
  • [x] Microsoft Teams integrations
  • [x] Custom webhook notifications
  • [x] Deployment notifications

51. ADVANCED — Workflow Cancellation

  • [x] Manual cancellation
  • [x] Automatic cancellation
  • [x] Concurrency cancellation
  • [x] cancel-in-progress
  • [x] Job cancellation
  • [x] Step cancellation
  • [x] Cancellation signals
  • [x] Cancellation conditions
  • [x] Cleanup after cancellation
  • [x] cancelled()
  • [x] Workflow cancellation lifecycle

52. ADVANCED — Pull Request CI Patterns

  • [x] PR validation
  • [x] Fork PR validation
  • [x] Draft PR workflows
  • [x] PR activity types
  • [x] Required checks
  • [x] Merge queues
  • [x] Merge-group checks
  • [x] Changed-file workflows
  • [x] PR labeling automation
  • [x] PR commenting
  • [x] PR test reports
  • [x] PR preview environments
  • [x] PR security scanning
  • [x] PR cleanup workflows

53. ADVANCED — Branch and Release Strategies

  • [x] Feature branches
  • [x] Trunk-based development
  • [x] GitFlow
  • [x] Release branches
  • [x] Environment branches
  • [x] Branch-based deployments
  • [x] Tag-based deployments
  • [x] Semantic version tags
  • [x] Hotfix workflows
  • [x] Promotion workflows

54. ADVANCED — Deployment Strategies

  • [x] Rolling deployment
  • [x] Blue/green deployment
  • [x] Canary deployment
  • [x] Progressive delivery
  • [x] Feature-flag deployment
  • [x] Environment promotion
  • [x] Immutable deployment
  • [x] Manual deployment
  • [x] Automated deployment
  • [x] Rollback workflows
  • [x] Deployment verification
  • [x] Smoke testing

55. ADVANCED — Testing GitHub Actions

  • [x] Workflow syntax validation
  • [x] Workflow linting
  • [x] actionlint
  • [x] YAML validation
  • [x] Custom action unit testing
  • [x] JavaScript action testing
  • [x] Docker action testing
  • [x] Composite action testing
  • [x] Reusable workflow testing
  • [x] Integration testing
  • [x] Staging workflow testing
  • [x] Local workflow emulation
  • [x] act
  • [x] Mock contexts
  • [x] Mock events
  • [x] Test repositories

56. ADVANCED — Workflow Maintenance

  • [x] Action version upgrades
  • [x] Deprecated action versions
  • [x] Runner image changes
  • [x] Node runtime changes
  • [x] Dependency updates
  • [x] Dependabot for Actions
  • [x] Workflow refactoring
  • [x] Workflow modularization
  • [x] Reusable workflow adoption
  • [x] Action pinning maintenance
  • [x] Secret rotation
  • [x] Runner upgrades
  • [x] Workflow documentation

57. ADVANCED — GitHub Actions Limits

  • [x] Workflow execution limits
  • [x] Job execution limits
  • [x] Matrix limits
  • [x] Concurrency limits
  • [x] Workflow queue limits
  • [x] Runner limits
  • [x] Self-hosted runner limits
  • [x] Artifact limits
  • [x] Cache limits
  • [x] Secret limits
  • [x] Variable limits
  • [x] Input limits
  • [x] Output limits
  • [x] Reusable workflow limits
  • [x] Workflow nesting limits
  • [x] Workflow retention limits
  • [x] API rate limits

58. ADVANCED — GitHub Actions Importer

  • [x] GitHub Actions Importer
  • [x] CI migration
  • [x] Migration planning
  • [x] Workflow auditing
  • [x] Workflow conversion
  • [x] Supplemental arguments
  • [x] Importer configuration
  • [x] Custom transformers
  • [x] Migration validation
  • [x] Migration from Jenkins
  • [x] Migration from GitLab CI/CD
  • [x] Migration from CircleCI
  • [x] Migration from Azure DevOps
  • [x] Migration from Travis CI

59. ADVANCED — Common CI/CD Design Patterns

  • [x] Build-test-deploy
  • [x] Build once, deploy many
  • [x] Fan-out/fan-in
  • [x] Matrix pipelines
  • [x] Reusable pipeline architecture
  • [x] Centralized workflow architecture
  • [x] Environment promotion
  • [x] Artifact promotion
  • [x] Conditional deployment
  • [x] Manual approval gates
  • [x] Automated rollback
  • [x] Scheduled automation
  • [x] Event-driven automation
  • [x] Cross-repository automation
  • [x] Dynamic pipeline generation

60. ADVANCED — GitHub Actions Anti-Patterns

  • [x] Duplicate workflow logic
  • [x] Overprivileged GITHUB_TOKEN
  • [x] Long-lived cloud credentials
  • [x] Unpinned third-party actions
  • [x] Mutable action references
  • [x] Secrets in workflow files
  • [x] Secrets in logs
  • [x] Untrusted input in shell commands
  • [x] Unsafe pull_request_target
  • [x] Persistent public self-hosted runners
  • [x] Excessive matrix jobs
  • [x] Cache misuse
  • [x] Cache poisoning
  • [x] Artifact misuse
  • [x] Hard-coded environment values
  • [x] Excessive workflow chaining
  • [x] Monolithic workflows
  • [x] Missing timeouts
  • [x] Missing concurrency controls
  • [x] Missing deployment protections

61. ADVANCED — Production Best Practices

  • [x] Least-privilege permissions
  • [x] OIDC cloud authentication
  • [x] SHA-pinned actions
  • [x] Reusable workflows
  • [x] Composite actions
  • [x] Environment protection
  • [x] Required reviewers
  • [x] Concurrency controls
  • [x] Job timeouts
  • [x] Dependency caching
  • [x] Artifact retention
  • [x] Ephemeral runners
  • [x] Runner isolation
  • [x] Secret rotation
  • [x] Branch protection
  • [x] Rulesets
  • [x] Required checks
  • [x] Workflow monitoring
  • [x] Workflow metrics
  • [x] CI/CD auditability
  • [x] Supply-chain security
  • [x] Artifact provenance
  • [x] Workflow versioning
  • [x] Deployment traceability
  • [x] Rollback readiness

62. ADVANCED — Reference and Administration

  • [x] Workflow syntax reference
  • [x] Events reference
  • [x] Workflow commands reference
  • [x] Variables reference
  • [x] Expressions reference
  • [x] Contexts reference
  • [x] Deployments reference
  • [x] Environments reference
  • [x] Dependency caching reference
  • [x] Reusable configurations reference
  • [x] Action metadata reference
  • [x] Workflow cancellation reference
  • [x] Dockerfile support reference
  • [x] GitHub-hosted runners reference
  • [x] Larger runners reference
  • [x] Self-hosted runners reference
  • [x] Security reference
  • [x] Secrets reference
  • [x] OIDC reference
  • [x] GitHub Actions limits
  • [x] GitHub Actions Importer reference
  • [x] Billing and usage
  • [x] Actions policies
  • [x] Actions metrics
  • [x] Runner administration
  • [x] Workflow administration
  • [x] Repository administration
  • [x] Organization administration
  • [x] Enterprise administration

Related Posts

GitHub Repository Settings — Complete Reference Guide & Tutorial

Last Verified: September 2026Platform: GitHub.com / GitHub Enterprise Cloud unless stated otherwiseAudience: Developers, DevOps engineers, repository administrators, security engineers, platform engineers, team leads, and trainers GitHub repository settings are the…

Read More

GitHub Organization Administration — Complete Reference Guide and Tutorial

Last Verified: September 2026Scope: GitHub.com organization administration, with GitHub Enterprise Cloud and GitHub Enterprise Server differences called out where they materially affect administration.Audience: GitHub organization owners, platform engineers, DevOps engineers,…

Read More

GitHub Packages — Complete Reference Guide & Hands-On Tutorial

Last Verified: September 2026Scope: GitHub.com / GitHub Enterprise Cloud unless explicitly stated otherwiseAudience: Developers, DevOps engineers, platform engineers, administrators, architects, trainers, and engineering teams GitHub Packages is GitHub’s package-hosting platform….

Read More

GitHub Projects — Complete Reference Guide & Tutorial

Scope: Current GitHub Projects / Projects v2, not Projects (classic).Audience: Developers, DevOps engineers, engineering managers, product managers, project administrators, platform teams, and GitHub organization owners.Last verified: 2026-09-26 against current GitHub documentation.Learning…

Read More

Git: Git Branching & Merging – A Complete Tutorials

PRACTICAL TECHNICAL HANDBOOK / 19 SEPTEMBER 2026 Git Branching & Merging Branch types, integration choices, conflicts and safe recovery Understand what Git changes, choose the right method,…

Read More

AWS Elastic IP Cross-Account / Cross-Organization Transfer Runbook

Use case: AWS Organization A / Account A → AWS Organization B / Account BObjective: Retain the exact same public IPv4 address while changing AWS account ownership….

Read More