DevOpsInterviewPrep logo
CI/CD, Release Engineering & GitOps / 04
easyNewGitLabMicrosoftAtlassian

Explain how GitHub Actions is put together: workflows, jobs, steps and runners.

Four nested things and one rule that explains most confusion: jobs run on different machines and share nothing unless you make them. Everything about artifacts and caching follows from that.

Updated Sep 2026 · Grounded in researched DevOps, SRE and platform engineering interview loops, written to a senior-engineer editorial bar, and never padded to hit a word count.

TL;DR: A workflow is a YAML file triggered by an event; it contains jobs that run in parallel on separate runners; each job is steps that run in order on one machine. Jobs share no filesystem, so anything passing between them goes through artifacts or a cache.

How to approach it

Give the hierarchy, then immediately state the isolation rule, because that is where beginners lose time. A build job producing a file and a deploy job not finding it is the first thing everybody hits. Then mention the security question, since interviewers ask it and it is what separates a casual user from someone who has run this for a team.

A strong answer

Workflow. A YAML file in .github/workflows/, triggered by an event: push, pull_request, schedule, workflow_dispatch for a manual run, or workflow_call to be invoked by another workflow. One repository can have many, and they are independent.

Job. A unit that runs on one runner. Jobs in a workflow run in parallel by default; needs: creates a dependency so one waits for another. Each job gets a clean machine.

Step. Runs in order within a job, on that job's machine. A step either runs a shell command with run: or invokes a reusable action with uses:.

Runner. The machine. GitHub-hosted runners are clean VMs per job, which is convenient and metered by the minute. Self-hosted runners are yours, which is what you move to for specific hardware, private network access, or when the bill gets uncomfortable.

name: ci
on:
  pull_request:
  push:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm test
  publish:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/build-and-push.sh

The isolation rule and what follows from it: jobs share nothing. test and publish are different machines with different disks. Passing a build output between them means actions/upload-artifact then actions/download-artifact, and speeding up dependency installs across runs means actions/cache keyed on a lockfile hash. Even the checkout has to be repeated, which surprises people, and is why a job doing nothing but deploying still starts with actions/checkout.

Two more pieces worth naming. A matrix runs the same job across combinations (node-version: [18, 20, 22]) in parallel, which is how you test several versions without duplicating YAML. And environments give a deployment target a name, with optional required reviewers and its own secrets, which is the mechanism for a production approval gate.

On security, because it gets asked: pin third-party actions to a full commit SHA rather than a tag, since a tag is mutable and uses: some/action@v1 means running whatever that tag points at today. Use OIDC to get short-lived cloud credentials rather than storing long-lived keys as secrets, which is the same workload identity idea Kubernetes uses: the runner proves who it is to the cloud and receives a credential that expires, so there is nothing long-lived to leak. And be careful with pull_request_target, which runs with repository secrets available in the context of a pull request from a fork, which is exactly the shape of a credential-theft bug.

What interviewers probe next

"How do you avoid copying the same workflow into thirty repositories?" Reusable workflows called with workflow_call, or a composite action for a step sequence. Centralising it also gives you one place to fix a security issue.

"When would you use self-hosted runners?" Specific hardware, access to a private network, large caches that are expensive to rebuild, or cost at volume. The trade is that you now own an autoscaling fleet and its security boundary, and a self-hosted runner on a public repository is a serious risk.

"What is the difference between a secret and a variable?" Secrets are encrypted, masked in logs, and unavailable to workflows triggered from forks. Variables are plain configuration. Putting a token in a variable exposes it in the log.

Common mistakes

Expecting files to persist between jobs, then debugging a missing build output that was on another machine.

Referring to third-party actions by tag, so a repository you do not control decides what runs with your secrets.

Running the full matrix on every push instead of on pull requests and main, which burns minutes and money.

Storing long-lived cloud keys as secrets when OIDC will issue short-lived ones.

That one was free, and so are 18 answers per topic without an account. Signing in doubles that to 28, keeps your bookmarks, and tracks which topics you keep getting wrong.one Google click · no card · nothing to cancel
HOW DID IT GO?
0
UP NEXT ON YOUR JOURNEY
DISCUSSION · 0

Nothing here yet. Say how you would answer it.