Getting to know the main GitHub Actions architecture: workflow, event, job, step, action, and runner, then understanding the difference between GitHub-hosted and self-hosted runners along with a labeled workflow anatomy.

In episode 1 you understood CI/CD concepts and why GitHub Actions is the main choice. Now it's time to understand how GitHub Actions works from the inside. Before writing even one line of YAML, there are six concepts you need to hold on to tightly: workflow, event, job, step, action, and runner. These six terms are the basic language you'll use in all the coming episodes — from the simplest workflow to complex production pipelines.
Think of GitHub Actions as an automation factory. In this episode we'll dissect that factory: what the blueprint is (workflow), who presses the button (event), how the work is divided (job and step), the ready-made components being called (action), and the machine that runs everything (runner).
| Component | Function | Analogy |
|---|---|---|
| Workflow | YAML file that defines the entire automation | Factory blueprint / cooking recipe |
| Event | GitHub event that triggers a workflow | The button that starts the machine |
| Job | A group of steps running on one runner | One production unit in a team |
| Step | An individual step: run a shell command or an action | A single task done by a worker |
| Action | The smallest reusable module called by a step | Ready-made machine / library |
| Runner | The server that executes a job | The machine where production runs |
Workflow is the top-level automation unit — a YAML file placed in the .github/workflows/ directory of your repository. The file name is free-form (e.g., ci.yml), and each workflow stands alone, can be triggered at any time, and can be turned on or off without affecting other workflows. In a single repository, you can have many workflows: one for CI, one for deploy, another for security scanning.
Event is something that happens on GitHub and triggers a workflow to run. Without events, a workflow is a blueprint that never executes. The most common events:
push — when commits are pushed to the repository.pull_request — when a PR is opened, updated, or merged.workflow_dispatch — run manually via a button or gh workflow run.release, issues, schedule — and many more, which will be covered in detail in episode 3.Job is a group of steps executed on the same runner with the same environment. Each job runs in a fresh environment — a clean operating system installation — and by default jobs run in parallel with each other. Jobs are the right unit for separating responsibilities: one lint job, one test job, one deploy job.
Step is an individual step inside a job, executed sequentially from top to bottom and sharing the same environment. Each step only does one thing: run a shell command via run, or call an action via uses. If a step fails, the following steps won't run.
Action is the smallest reusable module — a piece of ready-made code you can call inside a step with the uses syntax. Unlike run which executes raw commands in the shell, an action is an already-wrapped unit: it receives inputs via with, runs its logic, and produces outputs. Actions can come from GitHub Marketplace (e.g., actions/checkout@v4), from other repositories, or be custom actions you build yourself.
Runner is the server that executes the workflow. Every time a job runs, GitHub provides a fresh virtual machine on a runner, installs the requested system image, and discards it after the job finishes. You don't need to think about one VM versus the previous one — every job always starts from a clean, identical environment.
Let's put it all together in one real example. Observe each part carefully:
name: CI
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependensi
run: npm ci
- name: Jalankan unit test
run: npm testIf this workflow is pushed, what happens? Here's the flow:
push to the main branch occurs.test job.test job is asked to run on the runner ubuntu-latest — a clean Linux VM.checkout uses an action to clone the repository, setup-node prepares Node.js version 20, then two run steps install dependencies and run the tests.Note that your code is not automatically available on the runner — that's why the first step is always actions/checkout@v4, which clones the repository contents into the runner's working directory.
Runners in GitHub Actions come in two types:
| Aspect | GitHub-hosted | Self-hosted |
|---|---|---|
| Management | Fully by GitHub | Yours (VM, physical, or container) |
| OS images | Ubuntu, Windows, macOS, including arm64 | Free as needed |
| Cleanliness | Always fresh on every job | Environment can be reused (state may linger) |
| Cost | Uses minute quota | Free, only your own infrastructure cost |
| Network access | Public internet | Can reach private/VPC networks |
| Best for | Common & lightweight workflows | Special hardware, VPC, or cost savings |
Standard labels for GitHub-hosted runners include ubuntu-latest, windows-latest, macos-latest, and ubuntu-24.04-arm for the ARM64 architecture. Self-hosted runners, on the other hand, are registered to a repository or organization via a token, then referenced with custom labels you define yourself — for example runs-on: [self-hosted, gpu].
Warning
Never add a self-hosted runner to a public repository. Anyone who can open a pull request can execute arbitrary code on your machines — this is one of the most serious security risks in GitHub Actions. The details will be covered in episode 15.
Let's summarize the hierarchy from largest to smallest:
Workflow (file YAML)
├─ dipicu oleh Event (push, pull_request, ...)
└─ berisi 1..n Job
└─ tiap Job berjalan di 1 Runner
└─ berisi 1..n Step (berurutan)
└─ tiap Step: run <shell> ATAU uses <action>Understand this pattern: an event triggers a workflow → a workflow has jobs → a job runs on a runner → a job contains steps → a step runs a shell command or calls an action. All the following episodes just enrich these layers.
Tip
A helpful rule of thumb: when one thing fails, everything is easier to debug when jobs are small and steps are clear. Separate the lint, test, and deploy jobs rather than combining them into one giant job. This also makes partial re-runs much cheaper.
In episode 2 you mastered the core GitHub Actions architecture:
name and on, to jobs, runs-on, and steps combining uses and run.The architecture is understood; now it's time to practice. In episode 3 you'll write your first workflow — a hello world workflow that actually runs — while also diving into all kinds of event triggers and event filtering that make your workflow run only when needed. See you in episode 3!