1.2 GitHub Actions Architecture & Core Mental Model
Key Takeaways
- GitHub Actions operates on a four-tier hierarchical execution graph: Workflows contain one or more Jobs, Jobs run on specific Runners and contain sequential Steps, and Steps execute shell commands or reusable Actions.
- Workflows are event-driven, triggered by repository webhook events, POSIX schedules, or manual/API dispatches, which inject rich JSON event payloads into the `${{ github.event }}` context.
- Jobs execute in parallel by default on isolated, ephemeral runner virtual machines or containers, sharing no filesystem state unless explicitly coordinated via `needs` dependencies and artifact transfers.
- Steps within the same job execute sequentially on a single runner instance, sharing the filesystem, working directory (`$GITHUB_WORKSPACE`), and environment variables written to `$GITHUB_ENV`.
- Runners communicate with GitHub's backend strictly over outbound HTTPS (TCP port 443) via long polling or WebSockets, requiring zero inbound firewall ports to be exposed.
GitHub Actions Architecture & Core Mental Model
To pass the GH-200 exam and architect resilient CI/CD pipelines, you must understand the underlying execution engine of GitHub Actions. GitHub Actions is not simply a task runner; it is a distributed, event-driven orchestration system built on a strictly enforced component hierarchy and an asynchronous message-queue architecture.
1. The Four-Tier Component Hierarchy
Every automated pipeline in GitHub Actions conforms to a four-tier hierarchy: Workflow, Job, Step, and Action/Command.
+-----------------------------------------------------------------------------+
| GITHUB ACTIONS COMPONENT HIERARCHY |
| |
| +---------------------------------------------------------------------+ |
| | WORKFLOW (.github/workflows/*.yml) | |
| | Top-level automated process triggered by events | |
| | | |
| | +--------------------------+ +----------------------------+ | |
| | | JOB 1: 'build' | | JOB 2: 'test' (needs: build)| | |
| | | Runs on Runner VM A | | Runs on Runner VM B | | |
| | | | | | | |
| | | [Step 1: Checkout] | | [Step 1: Download Artifact| | |
| | | | | | | | | |
| | | [Step 2: Setup Node] | | [Step 2: Run Tests] | | |
| | | | | | | | | |
| | | [Step 3: Compile App] | | [Step 3: Post Summary] | | |
| | +--------------------------+ +----------------------------+ | |
| +---------------------------------------------------------------------+ |
+-----------------------------------------------------------------------------+
1. Workflow (The Orchestration Root)
- A workflow is an automated, configurable process defined by a YAML file located strictly in the repository's
.github/workflows/directory. - A repository can contain multiple workflows (e.g.,
ci.yml,release.yml,security-scan.yml). - Each workflow defines its own triggers (
on:), global environment variables, concurrency limits, defaults, permissions, and set of jobs.
2. Job (The Unit of Distributed Execution)
- A job is a set of steps that execute on a single designated Runner (virtual machine or container).
- Parallelism by Default: By default, multiple jobs defined inside the same workflow execute concurrently in parallel as soon as runner capacity becomes available.
- Sequential Execution via
needs: To enforce sequential execution or construct a Directed Acyclic Graph (DAG), you must declare dependencies explicitly using theneeds:keyword (e.g.,needs: [build, lint]). - Isolated Environments: Each job runs on a separate, fresh runner instance with its own isolated virtual machine or container environment. Filesystem modifications in Job A are completely inaccessible to Job B unless shared via artifact uploads or external storage.
3. Step (The Sequential Task Unit)
- A step is an individual task that runs sequentially inside the job's assigned runner environment.
- A step can be either:
- An Action (reusable unit invoked with
uses: actions/checkout@v4). - A Shell Command (executable script run with
run: npm test).
- An Action (reusable unit invoked with
- Shared Context: All steps within a single job share the same runner filesystem, the same working directory (
$GITHUB_WORKSPACE), and environment variables exported to$GITHUB_ENV. - Failure Propagation: If a step fails (returns a non-zero exit code), GitHub Actions immediately halts execution of subsequent steps in that job by default, unless overridden by
continue-on-error: trueor conditional status functions likeif: always().
4. Action (The Smallest Reusable Building Block)
- An action is a reusable, standalone application component packaged for execution inside a workflow step.
- Actions can be authored as Composite Actions (combining multiple run steps), JavaScript Actions (Node.js runtime), or Docker Container Actions (Linux container image).
2. Event-Driven Architecture & Webhook Payloads
GitHub Actions is fundamentally event-driven. Execution is initiated when specific activities occur within GitHub, on a schedule, or via an external API call.
+-----------------------------------------------------------------------------+
| EVENT INGESTION & DISPATCH |
| |
| [REPOSITORY ACTIVITY] [TIME SCHEDULE] [EXTERNAL DISPATCH] |
| - Git Push - POSIX Cron - REST API Webhook |
| - Pull Request Opened - Daily / Hourly - repository_dispatch |
| - Issue Comment |
| \ | / |
| \ | / |
| v v v |
| +-------------------------------------------------------------+ |
| | GITHUB EVENT INGESTION SERVICE | |
| | - Validates webhook payload & filters (branch/tag/path) | |
| | - Injects payload into `${{ github.event.* }}` context | |
| | - Queues workflow run execution graph | |
| +-------------------------------------------------------------+ |
+-----------------------------------------------------------------------------+
When an event occurs:
- GitHub's internal webhook system creates a structured JSON payload describing the actor, repository, commit SHA, branch reference, pull request metadata, or issue details.
- GitHub parses all workflow files in
.github/workflows/whoseon:trigger declarations match the event and satisfy any configured branch, tag, or path filters. - The complete event JSON payload is injected into the workflow's
${{ github.event }}context, allowing steps and conditional expressions (if:) to inspect payload attributes (e.g.,${{ github.event.pull_request.base.ref }}).
3. Runner Architecture & Outbound Network Topology
A Runner is the compute application that checks out your code, processes the steps defined in a job, reports status updates, and streams execution logs back to GitHub.
+-----------------------------------------------------------------------------+
| RUNNER OUTBOUND COMMUNICATION MODEL |
| |
| [ENTERPRISE DATA CENTER / CLOUD VPC] [GITHUB ENTERPRISE] |
| +---------------------------------------+ +-------------------+ |
| | Self-Hosted Runner Machine | | GitHub Service | |
| | | | Backend | |
| | +-------------------------------+ | | | |
| | | `actions-runner` Daemon | | | | |
| | +-------------------------------+ | | | |
| | | | | | |
| | | Outbound HTTPS | | | |
| | | (TCP Port 443) | | | |
| | | Long-Poll/WSS | | | |
| | +---------------------------->| Job Message Queue | |
| | | | & Log Ingestion | |
| | [NO INBOUND PORTS OPENED (Port 22/80 X| | | |
| +---------------------------------------+ +-------------------+ |
+-----------------------------------------------------------------------------+
The Outbound-Only Communication Principle
A critical architectural concept frequently tested on the GH-200 exam is the runner networking model:
- Runners communicate with GitHub Enterprise Cloud or GitHub Enterprise Server exclusively via outbound HTTPS connections on TCP port 443.
- The runner agent utilizes HTTP long-polling and secure WebSockets (
wss://) to poll GitHub's job dispatch queue. - No Inbound Ingress Required: Network administrators never need to open inbound firewall ports, configure public IP addresses, or create inbound port-forwarding rules to enable self-hosted runners. This architecture allows runners to operate securely inside restricted private enterprise subnets and air-gapped VPCs.
4. Runner Filesystem & Workspace Topography
When a job executes on a runner, GitHub Actions establishes a standardized directory structure. Understanding these environment variables and directory paths is essential for debugging and workflow authoring.
| Environment Variable | Directory Purpose & Structure | Lifecycle & Scope |
|---|---|---|
$GITHUB_WORKSPACE | The default working directory where actions/checkout clones your repository (e.g., /home/runner/work/repo-name/repo-name). | Preserved across all steps within the same job; wiped between jobs on hosted runners. |
$RUNNER_TEMP | A temporary scratch directory for transient files, scripts, and downloaded packages (e.g., /home/runner/work/_temp). | Available to all steps in the job; automatically deleted during job post-cleanup. |
$RUNNER_TOOL_CACHE | Directory containing pre-installed software toolchains (Node.js, Python, Java, Go, Ruby). | Read-only cache on hosted runners; managed via setup actions (e.g., actions/setup-node). |
$GITHUB_ENV | Path to a local file used to export environment variables to subsequent steps (echo "VAR=value" >> $GITHUB_ENV). | Processed by the runner agent after each step completes to update the step environment. |
$GITHUB_OUTPUT | Path to a local file used to export step outputs (echo "output_key=value" >> $GITHUB_OUTPUT). | Persisted in the step execution context for consumption via ${{ steps.<id>.outputs.* }}. |
$GITHUB_PATH | Path to a local file used to prepend directories to the system $PATH (echo "/custom/bin" >> $GITHUB_PATH). | Automatically updates the search path for all subsequent steps in the job. |
5. State Management: Job Isolation vs. Step Shared State
The fundamental boundary rule in GitHub Actions is the distinction between Step Shared Context and Job Isolation.
+-----------------------------------------------------------------------------+
| SHARED STEP CONTEXT VS. JOB ISOLATION |
| |
| +=====================================================================+ |
| | JOB A (Runner 1 VM) | |
| | Step 1: Creates file -> `$GITHUB_WORKSPACE/dist/app.js` | |
| | Step 2: Appends to `$GITHUB_ENV` -> `BUILD_NUM=42` | |
| | Step 3: Reads `$BUILD_NUM` and accesses `./dist/app.js` (SUCCESS) | |
| +=====================================================================+ |
| | |
| ISOLATION BOUNDARY (Separate VMs) |
| (Cannot read memory, env, or disk directly) |
| | |
| v |
| +=====================================================================+ |
| | JOB B (Runner 2 VM - `needs: [Job A]`) | |
| | Step 1: Looks for `./dist/app.js` (FAILS unless uploaded/downloaded) |
| | Step 2: Looks for `$BUILD_NUM` (FAILS unless passed via job outputs) |
| +=====================================================================+ |
+-----------------------------------------------------------------------------+
Comparison of Workflow Components
| Dimension | Workflow | Job | Step | Action |
|---|---|---|---|---|
| Scope | Repository / Global | Single Runner Instance | Single Command / Action | Reusable Subtask |
| Execution Mode | Triggered by events | Parallel by default; sequential with needs | Strictly Sequential | Synchronous within step |
| Filesystem State | N/A (Config file) | Isolated per VM / Container | Shared across steps in job | Shared within job workspace |
| Environment State | Declared in env: block | Declared in job env: | Updated via $GITHUB_ENV | Scoped to action inputs/env |
| Syntax Tag | Top-level YAML root | jobs.<job_id> | jobs.<job_id>.steps[] | uses: <owner>/<repo>@<ref> |
6. Execution Lifecycle Diagram
The following sequence illustrates how a repository event transitions from an initial Git action to distributed runner execution and final status reporting.
A network security administrator is configuring egress filtering rules on an enterprise firewall protecting a subnet of self-hosted GitHub Actions runners. Which communication requirement represents the correct architectural model for runner connectivity to GitHub Enterprise Cloud?
A workflow defines two jobs: build-app and publish-app. The build-app job compiles a binary at $GITHUB_WORKSPACE/bin/release. The publish-app job defines needs: build-app and immediately attempts to run ./bin/release --upload. What is the result when this workflow executes on standard GitHub-hosted runners?
Within a single workflow job, Step 1 creates a local configuration file at $GITHUB_WORKSPACE/config.json and Step 2 executes echo "STAGE=production" >> $GITHUB_ENV. What is the state of the runner environment when Step 3 executes inside the same job?