Study guide
5.4 Building Docker Container Actions
GitHub Actions Certification GH-200
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Introduction & Exam Overview
1.1 Exam Structure, Format & Scoring 1.2 GitHub Actions Architecture & Core Mental Model 1.3 Hands-On Preparation & Exam Day Strategy
2 Chapter 2: Workflow Triggers & Event Architecture
2.1 Webhook & Code Events: push, pull_request, and pull_request_target 2.2 Scheduled & Manual Triggers: schedule, workflow_dispatch, and repository_dispatch 2.3 Release, Issue, and Dynamic Lifecycle Events 2.4 Branch, Tag, and Path Filtering Patterns
3 Chapter 3: Jobs, Matrix Strategies & Execution Control
3.1 Job Anatomy, Step Execution & Job Dependencies (needs) 3.2 Contexts, Expressions & Functions 3.3 Environment Variables & Workflow Commands 3.4 Matrix Strategies & Multi-Platform Builds 3.5 YAML Anchors, Aliases & Merge Keys
4 Chapter 4: Reusable Workflows & Troubleshooting
4.1 Authoring & Calling Reusable Workflows (workflow_call) 4.2 Comparing Reusable Workflows, Composite Actions & Starter Workflows 4.3 Troubleshooting Workflow Runs & Debug Logging 4.4 Concurrency Controls & Status Check Functions
5 Chapter 5: Authoring Custom Actions
5.1 Action Architecture & action.yml Specification 5.2 Authoring Composite Actions 5.3 Building JavaScript Actions 5.4 Building Docker Container Actions
6 Chapter 6: Action Distribution, Marketplace & Advanced Action Types
6.1 Action Versioning, Semantic Releases & Tagging 6.2 Publishing Actions to GitHub Marketplace 6.3 Service Containers & Network Topology 6.4 actions/github-script & Workflow Tooling
7 Chapter 7: Runner Infrastructure & Self-Hosted Runners
7.1 GitHub-Hosted Runners & Larger Runners 7.2 Self-Hosted Runner Setup, Architecture & Communication 7.3 Self-Hosted Runner Security & Ephemeral Runners 7.4 Runner Groups, Labels & Organization Routing
8 Chapter 8: Enterprise Governance, Policies & Environments
8.1 Enterprise & Organization Action Policies 8.2 Environments, Protection Rules & Deployment Approvals 8.3 Repository Rulesets, Required Status Checks & Governance 8.4 Usage Limits, Billing Multipliers & Usage Metrics
9 Chapter 9: Secrets, Permissions & OIDC Cloud Authentication
9.1 Encrypted Secrets Architecture & Scopes 9.2 GITHUB_TOKEN & Least Privilege Configuration 9.3 OpenID Connect (OIDC) & Workload Identity Federation 9.4 Script Injection Prevention & Secure Workflow Patterns
10 Chapter 10: Supply Chain Hardening & Performance Optimization
10.1 Supply Chain Hardening: SHA Pinning, Dependabot & Artifact Attestations 10.2 Caching Dependencies with `actions/cache` 10.3 Artifacts Management & Job Handoff 10.4 Workflow Performance, Cost Optimization & Architecture Review
5.4 Building Docker Container Actions
Key Takeaways Explain This Topic AI
Docker container actions encapsulate execution logic, binaries, and operating system packages within a Linux container using runs.using: 'docker'.
Docker container actions can execute ONLY on Linux runners (such as ubuntu-latest or Linux self-hosted runners); attempting to run them on macOS or Windows hosted runners results in an immediate fatal error.
The image: parameter can specify a local Dockerfile (image: 'Dockerfile') built at runtime or a prebuilt container image from a public registry (image: 'docker://alpine:3.18').
The runner automatically maps declared action inputs to uppercase environment variables prefixed with INPUT_ (e.g., input target_env becomes $INPUT_TARGET_ENV), and actions write outputs to $GITHUB_OUTPUT.
The runner workspace ($GITHUB_WORKSPACE) is mounted into the container at /github/workspace, which serves as the default working directory.
Last updated: August 2026
Building Docker Container Actions
Docker Container Actions package your action's code, runtime environment, system binaries, and operating system dependencies into a self-contained Linux container. While JavaScript and Composite actions rely on tools pre-installed on the host runner, Docker actions allow you to control the exact Linux distribution, system libraries, CLI utilities, and compiler versions required by your automation.
Docker container actions are ideal for running tasks that require bespoke Linux software, specific C/C++ toolchains, custom Python/Ruby/Go environments, or strict filesystem isolation. However, they come with distinct performance trade-offs and operating system boundaries that are heavily emphasized on the GH-200 examination.
1. Operating System Boundary: The Linux-Only Restriction
The single most critical architectural constraint for Docker container actions is runner compatibility: +-----------------------------------------------------------------------------+
- | DOCKER CONTAINER ACTION OS COMPATIBILITY |
- | |
- | +---------------------------------+-----------------------------------+ |
- | | RUNNER OPERATING SYSTEM | EXECUTION STATUS | |
- | +---------------------------------+-----------------------------------+ |
- | | Ubuntu Linux (ubuntu-latest) | [SUCCESS] Native Docker daemon | |
- | | Linux Self-Hosted (with Docker) | [SUCCESS] Native Docker daemon | |
- | | macOS (macos-latest / macos-14) | [FATAL ERROR] Container unsupported| |
- | | Windows (windows-latest) | [FATAL ERROR] Container unsupported| |
- | +---------------------------------+-----------------------------------+ |
- +-----------------------------------------------------------------------------+
Caution
Linux Runner Requirement: Docker container actions can only run on Linux runners . GitHub-hosted macOS and Windows runners do not support running Docker container actions. If a workflow step executes a Docker container action on macos-latest or windows-latest , the runner fails immediately with: Container action is only supported on Linux .
2. Anatomy of a Docker Container Action
A Docker container action repository typically consists of three core files:
action.yml : The metadata manifest declaring runs.using: 'docker' .
Dockerfile : Defines the container image build steps, base image, and installed packages.
entrypoint.sh : The shell script executed when the container starts. +-----------------------------------------------------------------------------+
- | DOCKER ACTION DIRECTORY STRUCTURE |
- | |
- | custom-docker-action/ |
- | ├── action.yml # Manifest declaring inputs, outputs & docker|
- | ├── Dockerfile # Base image & toolchain setup |
- | ├── entrypoint.sh # Runtime execution script |
- | └── README.md # Usage documentation |
- +-----------------------------------------------------------------------------+
- The action.yml Specification name: 'Terraform Security Scanner'
- description: 'Runs tfsec and Checkov inside an isolated Alpine security container'
inputs:
scan-directory:
- description: 'Path to Terraform configuration files'
- required: false
- default: '.'
severity-threshold:
- description: 'Minimum severity level to trigger failure (LOW, MEDIUM, HIGH, CRITICAL)'
- required: true
- default: 'HIGH'
outputs:
violations-count:
description: 'Total number of detected security violations'
runs:
- using: 'docker'
- image: 'Dockerfile' # Or prebuilt image: 'docker://alpine:3.18'
- entrypoint: '/entrypoint.sh' # Overrides Dockerfile ENTRYPOINT
- args: # Passed as arguments to entrypoint
- ${{ inputs.scan-directory }}
- ${{ inputs.severity-threshold }}
- 3. The Dockerfile and entrypoint.sh Suite
- The Dockerfile Implementation # Use minimal, secure Alpine base image
- FROM alpine:3.19
- # Install runtime dependencies
- RUN apk add --no-cache \
- bash \
- curl \
- jq \
- git
- # Copy entrypoint script into container root
- COPY entrypoint.sh /entrypoint.sh
- # Ensure entrypoint script is executable
- RUN chmod +x /entrypoint.sh
- # Define container entrypoint
- ENTRYPOINT ["/entrypoint.sh"]
- The entrypoint.sh Script #!/usr/bin/env bash
- set -euo pipefail
- # 1. Access arguments passed via args: [] or input environment variables
- SCAN_DIR="${1:-$INPUT_SCAN_DIRECTORY}"
- SEVERITY="${2:-$INPUT_SEVERITY_THRESHOLD}"
- echo "Starting Security Scan in directory: ${SCAN_DIR}"
- echo "Enforcing Severity Threshold: ${SEVERITY}"
- # 2. Perform scanning operations inside mounted workspace (/github/workspace)
- cd "/github/workspace/${SCAN_DIR}"
- VIOLATIONS_FOUND=0
- # (Scanning logic executed here...)
- # 3. Export outputs via GITHUB_OUTPUT environment variable
- if [ -n "${GITHUB_OUTPUT:-}" ]; then
- echo "violations-count=${VIOLATIONS_FOUND}" >> "$GITHUB_OUTPUT"
- fi
- # 4. Handle exit codes
- if [ "$VIOLATIONS_FOUND" -gt 0 ]; then
- echo "::error::Detected ${VIOLATIONS_FOUND} security violations exceeding threshold ${SEVERITY}"
- exit 1
- fi
- echo "Security audit completed successfully with 0 violations."
- exit 0
- 4. Input Translation: The INPUT_<NAME> Convention
When a workflow step invokes a Docker container action with inputs, the GitHub Actions runner automatically converts those inputs into environment variables passed directly into the container process: +-----------------------------------------------------------------------------+
- | INPUT TO ENVIRONMENT MAPPING |
- | |
- | Caller Workflow with: Block Container Environment Variable |
- | --------------------------- ------------------------------ |
- | with: |
- | scan-directory: './infra' =====> $INPUT_SCAN_DIRECTORY="./infra" |
- | severity-threshold: 'HIGH' =====> $INPUT_SEVERITY_THRESHOLD="HIGH" |
- | api_key: 'secret123' =====> $INPUT_API_KEY="secret123" |
- +-----------------------------------------------------------------------------+
Transformation Rules:
The input name is converted to UPPERCASE .
Any hyphens ( - ) or spaces are replaced with underscores ( _ ).
The variable is prefixed with INPUT_ .
Inside your entrypoint.sh script, you can read inputs either by positional arguments ( $1 , $2 ) if defined in args: [] , or directly from $INPUT_<PARAMETER_NAME> .
5. Container Filesystem Mounts & Permissions
When GitHub Actions runs a Docker action, it mounts specific host runner directories into the container to enable file access and communication with the runner daemon:
Container Mount Path Host Source Path Purpose & Access
/github/workspace $GITHUB_WORKSPACE The checked-out repository code. The container's default working directory is set to /github/workspace .
/github/home $HOME (Runner user home) Preserves user profile configuration and tool settings.
/github/workflow $RUNNER_TEMP/_github_workflow Workflow runtime state and event JSON payloads ( $GITHUB_EVENT_PATH ).
/github/file_commands $RUNNER_TEMP/_github_home Mount point for $GITHUB_OUTPUT , $GITHUB_ENV , and $GITHUB_PATH files.
Important
Root Permissions: Docker container actions run as the root user ( UID 0 ) by default inside the container. Any files created inside /github/workspace by the container will be owned by root . If subsequent non-container steps on the host runner attempt to modify or delete these files, permission errors can occur unless ownership is adjusted.
6. Local Dockerfile vs Prebuilt Registry Images
In action.yml , the image: key supports two distinct modes of container resolution: # Mode 1: Local Dockerfile build
runs:
- using: 'docker'
- image: 'Dockerfile'
# Mode 2: Prebuilt registry image
runs:
- using: 'docker'
- image: 'docker://ghcr.io/enterprise-org/sec-scanner:v2.1.0'
- Comprehensive Comparison Table
Evaluation Dimension Local Dockerfile ( image: 'Dockerfile' ) Prebuilt Image ( image: 'docker://<uri>' )
Image Source Built dynamically on the runner at job startup Pulled directly from a container registry (Docker Hub, GHCR, ECR)
Startup Overhead High (Runs docker build on every job execution) Low / Moderate (Fast docker pull of pre-compiled layers)
Build Consistency Can break if upstream base packages or network repos change Deterministic (Immutable image digest/tag ensures consistency)
Maintenance Overhead Minimal (Only maintain Dockerfile in Git repo) Requires a separate CI/CD release workflow to build and push images
Private Registry Auth Not applicable Requires credentials if pulling from private registry registries
Best For Fast prototyping, simple lightweight Dockerfiles Production enterprise actions, heavy toolchains, complex compilers