Back to Software Engineering & Delivery Mastery Series GitHub Actions Bootcamp

Module 1: Introduction to GitHub Actions

June 2, 2026 Wasil Zafar 35 min read

What GitHub Actions is, why it dominates CI/CD, its key components, YAML workflow syntax, creating and debugging your first workflow, and choosing between self-hosted and GitHub-hosted runners.

Table of Contents

  1. What is GitHub Actions
  2. GitHub Actions vs Other CI/CD Tools
  3. Key Components
  4. Understanding YAML Syntax
  5. Creating Your First Workflow
  6. Running & Debugging Workflows
  7. Web UI & GitHub CLI
  8. Triggers & Events Overview
  9. Self-hosted vs GitHub-hosted
  10. Exercises

What is GitHub Actions and Why It Matters

GitHub Actions is a workflow automation platform built directly into GitHub. It allows you to automate software development workflows — build, test, package, release, and deploy — triggered by events in your repository. Unlike standalone CI/CD tools that require separate infrastructure and authentication, GitHub Actions lives where your code already lives.

Think of it this way: before GitHub Actions, setting up CI/CD was like installing a security system from a third-party company — they needed access to your house, their own cameras, their own wiring. GitHub Actions is like having the security system built into the house from day one. Same cameras, same wiring, same control panel as everything else.

Key Insight: GitHub Actions' killer advantage isn't its YAML syntax or its runner fleet — it's the network effect. With 100M+ developers on GitHub, the Actions Marketplace has become the largest ecosystem of reusable CI/CD components ever created. Any problem you face likely has a community action already solving it.

GitHub Actions launched in November 2019 and within three years became the dominant CI/CD platform for open-source software. Before it existed, teams chose between hosted CI services (Travis CI, CircleCI) that required code syncing, or self-managed tools (Jenkins) demanding dedicated infrastructure. GitHub Actions eliminated that tradeoff entirely.

Market Position and Adoption

As of 2026, GitHub Actions processes over 2 billion workflow runs per month. It's the default CI/CD for virtually all open-source projects and has overtaken Jenkins in new enterprise adoption. Key adoption drivers:

  • Zero setup for GitHub repos — Add a YAML file, get CI/CD instantly
  • Generous free tier — 2,000 minutes/month for public repos, unlimited for open source
  • Deep ecosystem integration — PR checks, branch protection, deployments API, packages registry
  • Marketplace network effect — 20,000+ community actions covering every conceivable task
  • Matrix builds — Test across OS/language/version combinations with a single definition

GitHub Actions vs Other CI/CD Tools

Every CI/CD platform makes different tradeoffs. Understanding where GitHub Actions excels — and where alternatives might be better — helps you make informed decisions.

Comparison Matrix
Feature GitHub Actions GitLab CI Jenkins CircleCI
Setup Complexity Minimal (YAML file) Minimal (YAML file) High (server + plugins) Low (config.yml)
Infrastructure Managed + self-hosted Managed + self-hosted Self-managed only Managed + self-hosted
Reusability Marketplace actions + reusable workflows CI/CD components + includes Shared libraries Orbs
Container Support Job + service containers Native (every job in container) Plugin-based Native Docker executor
Scalability Auto-scales (hosted) or ARC (self-hosted) Auto-scales (hosted) Manual scaling Auto-scales
Pricing (private repos) Included minutes + overage Included minutes + overage Free (infra cost) Credits-based

When to Choose GitHub Actions

Choose GitHub Actions when:

  • Your code is already on GitHub (biggest factor)
  • You want CI/CD without managing infrastructure
  • You need cross-platform testing (Linux, Windows, macOS)
  • You value community ecosystem and reusable components
  • You want tight integration with PRs, issues, and deployments

Consider alternatives when:

  • You need the entire DevOps lifecycle in one platform (GitLab)
  • You have extreme customization needs and existing Jenkins expertise
  • You're in a regulated environment requiring fully air-gapped CI (Jenkins/Tekton)
  • You need advanced pipeline visualization and insights (CircleCI)

Key Components: Workflows, Jobs, Steps, and Runners

GitHub Actions has a clear hierarchy of concepts. Understanding how they relate is essential before writing your first workflow.

GitHub Actions Architecture Hierarchy
flowchart TD
    A[Repository Event] -->|Triggers| B[Workflow]
    B --> C[Job 1: Build]
    B --> D[Job 2: Test]
    B --> E[Job 3: Deploy]
    C --> F[Step 1: Checkout]
    C --> G[Step 2: Setup Node]
    C --> H[Step 3: npm install]
    C --> I[Step 4: npm build]
    D --> J[Step 1: Checkout]
    D --> K[Step 2: Run Tests]
    E --> L[Step 1: Deploy]

    F -.->|Runs on| M[Runner: ubuntu-latest]
    J -.->|Runs on| N[Runner: ubuntu-latest]
    L -.->|Runs on| O[Runner: self-hosted]

    style A fill:#3B9797,color:#fff
    style B fill:#132440,color:#fff
    style M fill:#16476A,color:#fff
    style N fill:#16476A,color:#fff
    style O fill:#BF092F,color:#fff
                            

The six core components:

  • Workflow — A configurable automated process defined in a YAML file under .github/workflows/. A repo can have unlimited workflows running independently.
  • Event — A specific activity that triggers a workflow: code push, pull request opened, issue created, scheduled cron, manual dispatch, or external webhook.
  • Job — A set of steps that execute on the same runner machine. Jobs run in parallel by default. Use needs: to create sequential dependencies.
  • Step — A single task within a job. Either runs a shell command (run:) or invokes a reusable action (uses:). Steps execute sequentially within a job.
  • Action — A reusable, composable unit of automation. Can be JavaScript, Docker, or composite. Shared via GitHub Marketplace or defined locally in your repo.
  • Runner — The compute environment that executes a job. GitHub provides hosted runners (Ubuntu, Windows, macOS) or you deploy self-hosted runners on your own infrastructure.
Mental Model: Think of a workflow like a factory floor. The event is the order arriving. Jobs are independent workstations. Steps are the sequential operations at each workstation. Runners are the machines doing the physical work. Actions are the reusable tools and jigs that workers share.

Runners: The Execution Environment

GitHub-hosted runners are fresh virtual machines provisioned for each job, pre-loaded with development tools, and automatically destroyed after completion — guaranteeing a clean environment every time.

Runner Label OS vCPUs RAM Storage
ubuntu-latestUbuntu 22.04416 GB14 GB SSD
ubuntu-24.04Ubuntu 24.04416 GB14 GB SSD
windows-latestWindows Server 2022416 GB14 GB SSD
macos-latestmacOS 14 (Sonoma)3 (M1)7 GB14 GB SSD
macos-13macOS 13 (Ventura)4 (Intel)14 GB14 GB SSD

Understanding YAML Syntax for Workflows

If you've never written YAML before, don't worry — it's designed to be human-readable. YAML uses indentation (spaces, never tabs) to represent structure, colons for key-value pairs, and dashes for list items.

YAML Fundamentals

# Scalar values (strings, numbers, booleans)
name: My Workflow          # String (quotes optional for simple values)
timeout-minutes: 30        # Number
continue-on-error: false   # Boolean

# Lists (arrays) — two equivalent syntaxes
branches:                  # Block style
  - main
  - develop
  - 'release/**'

tags: [v1.0, v2.0]        # Flow style (inline)

# Maps (objects) — nested with indentation
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

# Multi-line strings
description: |             # Literal block (preserves newlines)
  This is line one.
  This is line two.

command: >                 # Folded block (joins lines with spaces)
  echo "this is a very long command
  that spans multiple lines
  but runs as one line"
Common YAML Pitfalls:
  • Tabs vs spaces — YAML requires spaces for indentation. A single tab will cause a parse error.
  • Colon in values — If your value contains :, wrap it in quotes: "Node.js: 20"
  • Boolean trapson, off, yes, no are parsed as booleans. Quote them if meant as strings.
  • Special characters — Values starting with {, [, *, &, !, %, @ need quoting.

Workflow File Structure

Every workflow file has three required top-level keys: name (optional but recommended), on (the trigger), and jobs (what to execute).

# .github/workflows/ci.yml — Minimal complete workflow
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build

Creating Your First Workflow

Let's build a real workflow from scratch. We'll create a CI pipeline for a Node.js project that runs on every push and pull request.

Step 1: Create the workflow directory

# From your repository root
mkdir -p .github/workflows

Step 2: Create the workflow file

# .github/workflows/ci.yml
name: Node.js CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18, 20, 22]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Upload coverage
        if: matrix.node-version == 20
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

Step 3: Commit and push

# Stage, commit, and push
git add .github/workflows/ci.yml
git commit -m "ci: add Node.js CI workflow"
git push origin main

Within seconds of pushing, navigate to your repository's Actions tab. You'll see the workflow running with three parallel jobs (one per Node.js version).

What just happened: GitHub detected a new file in .github/workflows/, parsed the YAML, matched the push event to the main branch filter, provisioned three fresh Ubuntu VMs (one per matrix entry), and began executing steps on each. All within 5-10 seconds of your push.

Running and Debugging Workflows

When workflows fail, you need systematic debugging strategies. GitHub provides several tools for troubleshooting.

Reading Workflow Logs

Every step produces logs visible in the Actions tab. Click a failed step to expand its output. Key things to look for:

  • Exit codes — Non-zero exit codes cause step failure. The log shows which command failed.
  • Annotations — Errors and warnings appear as annotations on the workflow summary.
  • Timing — Each step shows execution duration, helping identify bottlenecks.

Enabling Debug Logging

# Option 1: Repository secret (enables for all runs)
# Set secret: ACTIONS_RUNNER_DEBUG = true
# Set secret: ACTIONS_STEP_DEBUG = true

# Option 2: Re-run with debug logging
# In the Actions UI, click "Re-run jobs" → check "Enable debug logging"

# Option 3: Workflow-level debug output
- name: Debug context
  run: |
    echo "Event: ${{ github.event_name }}"
    echo "Ref: ${{ github.ref }}"
    echo "SHA: ${{ github.sha }}"
    echo "Actor: ${{ github.actor }}"
    echo "Runner OS: ${{ runner.os }}"
    echo "Runner Arch: ${{ runner.arch }}"

Common Failure Patterns

Debugging Checklist
SymptomLikely CauseFix
Workflow never triggersYAML syntax error or wrong event filterValidate YAML, check branch/path filters
"Permission denied"Missing permissions: or wrong token scopeAdd required permissions block
"Command not found"Tool not installed on runnerAdd setup step (setup-node, setup-python, etc.)
Tests pass locally but fail in CIEnvironment differencesCheck Node/Python version, OS, env vars
Workflow is slowNo caching, large checkout, serial jobsAdd caching, use sparse checkout, parallelize

Managing Workflows via Web UI and GitHub CLI

You can manage workflows through both the GitHub web interface and the gh CLI tool.

Web UI Actions Tab

The Actions tab provides: workflow run history with filtering, per-step log inspection, re-run capabilities (full or failed-only), manual workflow dispatch UI, caching insights, and runner management.

GitHub CLI (gh)

# List recent workflow runs
gh run list --limit 10

# View a specific run
gh run view 12345678

# Watch a run in real-time
gh run watch 12345678

# Re-run failed jobs
gh run rerun 12345678 --failed

# Trigger a workflow manually (workflow_dispatch)
gh workflow run deploy.yml -f environment=staging -f version=1.2.3

# List workflows in the repo
gh workflow list

# View workflow definition
gh workflow view ci.yml

# Download artifacts from a run
gh run download 12345678 -n coverage-report

# Cancel a running workflow
gh run cancel 12345678
Pro Tip: Use gh run watch during development to see your workflow execute in real-time without switching to the browser. Combined with gh run rerun --failed, you can iterate on failing workflows entirely from the terminal.

Workflow Triggers and Events Overview

GitHub Actions supports over 35 different event types that can trigger workflows. The most commonly used are:

Event Categories
flowchart LR
    A[Events] --> B[Repository Events]
    A --> C[Scheduled]
    A --> D[Manual]
    A --> E[External]

    B --> B1[push]
    B --> B2[pull_request]
    B --> B3[release]
    B --> B4[issues]

    C --> C1[schedule/cron]

    D --> D1[workflow_dispatch]
    D --> D2[repository_dispatch]

    E --> E1[webhook]
    E --> E2[workflow_call]

    style A fill:#132440,color:#fff
    style B fill:#3B9797,color:#fff
    style C fill:#16476A,color:#fff
    style D fill:#BF092F,color:#fff
    style E fill:#3B9797,color:#fff
                            
# Common trigger patterns
on:
  # Run on push to main
  push:
    branches: [main]
    paths: ['src/**', 'package.json']  # Only when these files change

  # Run on PRs targeting main
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # Run on schedule (UTC)
  schedule:
    - cron: '0 6 * * 1-5'  # 6 AM UTC, Mon-Fri

  # Manual trigger with inputs
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options: [staging, production]

  # Called by another workflow
  workflow_call:
    inputs:
      version:
        required: true
        type: string

We'll explore each trigger type in depth in Module 2: Workflow Triggers and Events.

Self-hosted vs GitHub-hosted Runners

The choice between runner types has significant implications for cost, security, performance, and maintenance burden.

Aspect GitHub-hosted Self-hosted
SetupZero — just specify runs-on:Provision machines, install runner agent
CostIncluded minutes (overage: $0.008/min Linux)Your infrastructure cost only
MaintenanceGitHub manages OS updates, toolsYou manage everything
Clean environmentFresh VM every job (guaranteed clean)Persistent (requires cleanup discipline)
Network accessPublic internet onlyCan access private networks, VPCs
HardwareStandard specs (4 vCPU, 16 GB)Custom hardware (GPUs, ARM, high-memory)
Startup time20-40 seconds (queue + provision)2-5 seconds (already running)
SecurityIsolated, ephemeral VMsYour responsibility to isolate
Security Warning: Never use self-hosted runners for public repositories. Any fork can submit a PR that executes arbitrary code on your self-hosted runner. Self-hosted runners should only serve private repos with trusted contributors.

When to Use Self-hosted Runners

  • Private network access — Deploying to resources behind a firewall or VPC
  • Specialized hardware — GPU builds, ARM compilation, high-memory workloads
  • Cost optimization at scale — Organizations running 100K+ minutes/month save significantly
  • Compliance requirements — Data residency, air-gapped environments
  • Performance — Persistent caches, pre-installed tools, faster startup
# Using self-hosted runners
jobs:
  deploy:
    runs-on: [self-hosted, linux, x64, production]
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh

  gpu-training:
    runs-on: [self-hosted, linux, gpu, a100]
    steps:
      - uses: actions/checkout@v4
      - run: python train_model.py

Exercises

Exercise 1: Hello World Workflow

Create a workflow that triggers on push to main and prints "Hello, GitHub Actions!" along with the current date, the runner OS, and the commit SHA that triggered the workflow. Verify it appears in the Actions tab.

Exercise 2: Multi-Language CI

Build a workflow that sets up both Node.js 20 and Python 3.12 in the same job. Install dependencies for a project that has both package.json and requirements.txt. Run tests for both.

Exercise 3: Manual Deployment Trigger

Create a workflow_dispatch workflow with two inputs: environment (choice: staging/production) and version (string). The workflow should echo the inputs and simulate a deployment. Trigger it from both the web UI and the gh CLI.

Exercise 4: Debug a Failing Workflow

Intentionally create a workflow with three common mistakes: (1) a YAML indentation error, (2) a missing uses: action version, and (3) a command that doesn't exist on the runner. Fix each one using the error messages in the Actions log. Document what each error message looks like.

Next in the Bootcamp

In Module 2: Workflow Triggers and Events, we'll dive deep into all 35+ event types, filtering by branches/paths/tags, security considerations for fork PRs, and advanced patterns like repository dispatch and cross-workflow triggers.