GitHub Actions Fundamentals
Topic 47

What GitHub Actions Is

Actions

GitHub Actions is event-driven automation that lives inside the repository. Something happens — a push, a pull request, a tag, a cron tick — and GitHub matches that event against the triggers you declared in a YAML file, then spins up a fresh virtual machine to run the steps you wrote. There is no separate CI server to provision, patch, and keep online; the platform owns the VM and the orchestration, and you own the YAML.

The model is worth internalizing before you write a single workflow: repo activity emits events, your on: declarations subscribe to them, and each match queues a run on an ephemeral runner that is destroyed when the run finishes. Everything else — caching, artifacts, matrix builds, reusable actions — is detail layered on top of that one loop.

The Event-Workflow Model

A workflow does nothing until an event it subscribes to is emitted. The on: key is that subscription. When repo activity produces a matching event, GitHub queues a workflow run; when nothing matches, nothing runs and you pay nothing. This is the difference from a polling CI server that wakes on a timer and asks "anything new?" — Actions is pushed the event the instant it happens, so a run is queued within seconds of the commit.

A minimal workflow: run tests on every push
name: CI
on: push
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

What GitHub Hosts

GitHub provides the runner VM, the orchestration that schedules and sequences jobs, and the log and artifact storage you read afterward. Public repositories get unlimited runner minutes; private repositories include 2,000 free minutes per month on the standard Linux runner, after which usage bills against the account. The OS multiplier matters: Linux is the baseline, Windows runners bill at roughly 2x, and macOS at roughly 10x, so an idle macOS matrix leg is a real line item.

Because the pipeline is hosted, you are not responsible for the box it runs on. You do not patch the runner OS, scale the fleet, or keep a Jenkins controller alive at 3 a.m. The trade is that you accept GitHub's runner images and their preinstalled toolchain, and you reach for self-hosted runners only when you need hardware or network access the hosted fleet cannot give you.

Ephemeral Execution

Every job runs on a clean VM that is thrown away when the job ends. Nothing survives between runs, and nothing is shared between jobs in the same run, unless you deliberately persist it: the dependency cache, an uploaded artifact, or a small value passed through job outputs. This is the single fact that catches newcomers — a file written by one job is simply not there for the next, because the next job is a different machine.

The upside of throwaway VMs is reproducibility. A green run on a fresh runner means the build works from a clean checkout with no leftover state, which is exactly the property a CI system exists to guarantee.

Where It Lives

Workflows live in .github/workflows/ as *.yml files, versioned alongside the code they build. A file placed anywhere else is silently ignored — there is no error, the workflow simply never runs. Keeping the pipeline in the repo means a change to CI goes through the same pull request, review, and rollback as a change to the application, which is a meaningful improvement over a pipeline configured by clicking through a server UI.

The Marketplace

Steps that say uses: pull in reusable units of automation called actions, published by GitHub, by vendors, and by the community. actions/checkout fetches your code; actions/setup-node installs a Node toolchain; thousands more cover deploys, notifications, and linters. An action runs inside your job with access to your token and secrets, which is why how you reference one — covered later in this chapter — is a security decision, not just a convenience.

Common Mistakes
  • Treating the runner as persistent — writing a file in one job and expecting a later job to read it; jobs run on separate VMs with no shared filesystem, so the file is gone.
  • Placing a workflow file outside .github/workflows/ and waiting for a run that never queues, because files anywhere else are silently ignored.
  • Assuming runner minutes are free on private repos, then being surprised by a bill — usage past 2,000 minutes/month is charged, and Windows costs 2x and macOS 10x against that budget.
  • Running an expensive workflow on every push to every branch instead of scoping on:, burning paid minutes on builds nobody reads.
  • Expecting state from a previous run to carry over, instead of caching dependencies or uploading artifacts when you genuinely need cross-run persistence.
Best Practices
  • Scope triggers with on: branch and paths: filters so a workflow fires only when relevant files change.
  • Keep workflows in source control and review changes to them in pull requests, exactly like application code.
  • Use the free, unlimited hosted runners for public repos rather than self-hosting, unless you need specific hardware or private network access.
  • Set concurrency: on deploy workflows early so two overlapping runs cannot race against the same environment.
  • Prefer the Linux runner for routine jobs and reserve the 2x/10x Windows and macOS runners for builds that genuinely require those platforms.
Comparable toolsGitLab CI/CD .gitlab-ci.yml plus runnersCircleCI hosted pipelines from config.ymlJenkins self-hosted controller and agentsTravis CI hosted, YAML-configuredAzure Pipelines hosted or self-hosted agents

Knowledge Check

Job A writes a file to disk; job B in the same workflow run needs it. What actually happens by default?

  • Job B cannot see it — each job runs on its own fresh VM, so an artifact, cache, or output is needed
  • Job B reads the file directly off the shared run filesystem, with no extra configuration on either job
  • The runner automatically syncs the file to every other job in the same run
  • Job B aborts the run, because writing files for another job to read is forbidden

Where must a workflow file live to run at all?

  • In .github/workflows/ with a .yml/.yaml extension; elsewhere is ignored
  • Anywhere in the repo root, as long as the file ends in .yml
  • In a top-level ci/ directory that you register under repo Settings → Actions
  • In .git/workflows/, alongside Git's internal hooks and refs

Which runner OS consumes included minutes fastest on a private repo?

  • macOS, at roughly 10x the Linux rate; Windows is about 2x and Linux the baseline
  • Linux, because its larger preinstalled toolchain raises its per-minute billing rate
  • Windows, billed at 10x, with macOS the cheaper of the two at 2x
  • All three, since they bill at one identical per-minute rate

What does the event-driven model give you over a CI server that polls on a timer?

  • A run queues within seconds of the event, and idle periods run and bill nothing
  • Builds run on a fixed timer regardless of whether any repo activity happened
  • It removes the need to declare any on: triggers in the workflow at all
  • It keeps a dedicated VM running continuously to watch the repo for changes

You got correct