5.1 Action Architecture & action.yml Specification

Key Takeaways

  • The action.yml (or action.yaml) metadata manifest is the mandatory declarative contract defining an action's name, description, inputs, outputs, branding, and execution runtime (runs.using).
  • GitHub Actions supports three distinct action execution models: JavaScript actions (Node.js runtime), Docker container actions (Linux container runtime), and Composite actions (multi-step shell and action pipelines).
  • Action inputs support validation keys including description, required, default, and deprecationMessage, while outputs define values mapped from execution steps or runtime code.
  • Actions can be published in dedicated public or private repositories (referenced via owner/repo@ref) or stored in subdirectories of a workflow repository (referenced locally via uses: ./.github/actions/my-action).
  • The branding block configures visual metadata for GitHub Marketplace, specifying a Feather icon and background badge color.
Last updated: August 2026

Action Architecture & action.yml Specification

In GitHub Actions, Actions represent the smallest reusable unit of computational work within a workflow step. While workflows orchestrate jobs and execution graphs, actions encapsulate specialized logic—such as configuring a toolchain, compiling binaries, interacting with the GitHub REST API, scanning container images, or publishing deployment artifacts.

Authoring custom actions allows organizations to standardize CI/CD patterns, eliminate boilerplate code across repositories, encapsulate security policies, and share automated solutions across the broader GitHub ecosystem. On the GitHub Actions Certification (GH-200) exam, custom action authoring accounts for a substantial portion of Domain 3 ("Author and Maintain Actions"). Candidates must demonstrate deep mastery of metadata schemas, runtime engine selection, and referencing syntax.


1. The Action Manifest: action.yml

Every custom action requires a metadata manifest file named either action.yml or action.yaml placed in the root of the action's repository or within a dedicated subfolder. This file is written in YAML syntax and defines the contract between the action author and the workflow caller.

+-----------------------------------------------------------------------------+
|                       ACTION.YML MANIFEST ARCHITECTURE                      |
|                                                                             |
|   +---------------------------------------------------------------------+   |
|   | METADATA IDENTIFIERS                                                |   |
|   | - name: 'Enterprise Security Linter'                                |   |
|   | - author: 'DevOps Security Team'                                    |   |
|   | - description: 'Audits source code for credential leaks and CVEs'   |   |
|   +---------------------------------------------------------------------+   |
|                                      |                                      |
|                                      v                                      |
|   +----------------------------------+----------------------------------+   |
|   |                                  |                                  |   |
|   v                                  v                                  v   |
| +--------------------+    +--------------------+    +--------------------+  |
| | INPUTS DEFINITION  |    | OUTPUTS DEFINITION |    | BRANDING DEFINITION|  |
| | - target-path      |    | - scan-status      |    | - icon: 'shield'   |  |
| | - fail-on-critical |    | - vulnerabilities  |    | - color: 'red'     |  |
| +--------------------+    +--------------------+    +--------------------+  |
|                                      |                                      |
|                                      v                                      |
|   +---------------------------------------------------------------------+   |
|   | EXECUTION ENGINE: runs.using                                        |   |
|   | - 'composite'  (Combines run / uses steps)                          |   |
|   | - 'node20'     (Executes JavaScript bundle on Node 20 runtime)      |   |
|   | - 'docker'     (Builds or pulls Linux container image)              |   |
|   +---------------------------------------------------------------------+   |
+-----------------------------------------------------------------------------+

Top-Level Manifest Keys

KeyRequiredTypeDescription & Purpose
nameYesStringThe human-readable name of the action displayed in workflow logs and the GitHub Marketplace.
authorNoStringThe name of the individual developer or organization maintaining the action.
descriptionYesStringA detailed summary explaining what the action does, its prerequisites, and its operational behavior.
inputsNoObjectDefines the input parameters accepted by the action, including data types, defaults, and requirements.
outputsNoObjectDefines the output variables produced by the action and exposed to subsequent workflow steps.
runsYesObjectConfigures the underlying execution engine, runtime environment, entrypoint scripts, and lifecycle hooks.
brandingNoObjectCustomizes the visual badge icon and background color when published to the GitHub Marketplace.

2. Deep Dive: inputs and outputs Schema

Parameterizing with inputs

The inputs: map defines parameters callers can pass via the with: keyword in a workflow step. Each input key supports four configuration parameters:

inputs:
  api-token:
    description: 'Authentication token for API communication'
    required: true
  scan-mode:
    description: 'Scanning mode verbosity'
    required: false
    default: 'standard'
  legacy-flag:
    description: 'Deprecated flag replaced by scan-mode'
    required: false
    deprecationMessage: 'The legacy-flag input is deprecated. Use scan-mode instead.'
  1. description (Required): Clear explanation of the input's purpose and expected values.
  2. required (Optional, Boolean): If true, the caller workflow step must provide this input; if omitted, GitHub Actions aborts the job before execution with a validation error. Defaults to false.
  3. default (Optional, String): The fallback value injected if the caller does not supply this input. Note that all defaults in action.yml are interpreted as strings.
  4. deprecationMessage (Optional, String): When a workflow supplies this input, the runner logs a warning annotation containing this message, informing developers to migrate to a newer parameter.

Declaring outputs

The outputs: block registers data generated during execution for use downstream:

outputs:
  scan-passed:
    description: 'Boolean flag indicating whether the security scan succeeded'
    value: ${{ steps.audit-step.outputs.passed }} # Mandatory for composite actions
  findings-count:
    description: 'Total number of detected vulnerabilities'
    value: ${{ steps.audit-step.outputs.total_cves }}

[!IMPORTANT] For Composite Actions, the value: parameter is mandatory for every output in action.yml. It binds the action's public output key to a specific internal step's output (${{ steps.<step_id>.outputs.<output_key> }}). For JavaScript and Docker actions, outputs are registered dynamically at runtime via the core SDK or $GITHUB_OUTPUT.


3. The branding Block for GitHub Marketplace

If you publish an action to the GitHub Marketplace, the branding block defines the visual badge rendered on the listing:

branding:
  icon: 'shield'
  color: 'purple'
  • icon: Must match an official Feather icon name (e.g., activity, alert-circle, archive, award, box, check-circle, cloud, code, cpu, database, download, file-text, git-branch, globe, layers, lock, package, play, server, shield, terminal, tool, upload-cloud, zap).
  • color: Restricted to standard Marketplace badge colors: white, yellow, blue, green, orange, red, purple, or gray-dark.

4. Comparing the Three Action Types

GitHub Actions supports three distinct runtime paradigms under runs.using:

+-----------------------------------------------------------------------------+
|                    COMPARISON OF ACTION EXECUTION TYPES                     |
|                                                                             |
|   +-----------------------+-----------------------+---------------------+   |
|   |   JAVASCRIPT ACTION   |   COMPOSITE ACTION    |    DOCKER ACTION    |   |
|   +-----------------------+-----------------------+---------------------+   |
|   | - runs.using: 'node20'| - runs.using: 'comp..'| - runs.using: 'doc..'|   |
|   | - Native Node runtime | - Multiple run/uses   | - Linux container   |   |
|   | - Linux, macOS, Win   | - Linux, macOS, Win   | - Linux ONLY        |   |
|   | - Sub-second startup  | - Sub-second startup  | - Container pull/bld|   |
|   | - Bundled dist/index  | - Shell scripts       | - System tooling    |   |
|   +-----------------------+-----------------------+---------------------+   |
+-----------------------------------------------------------------------------+

Comprehensive Action Types Comparison Matrix

CharacteristicJavaScript ActionsComposite ActionsDocker Container Actions
runs.using Identifier'node20''composite''docker'
Operating System SupportLinux, macOS, WindowsLinux, macOS, WindowsLinux ONLY (Fails on macOS/Windows)
Startup OverheadExtremely Low (< 1 second)Extremely Low (< 1 second)High (Image pull or local docker build)
Execution SpeedVery Fast (Direct Node.js execution)Fast (Executes native shell processes)Moderate (Container namespace isolation)
Runtime EnvironmentNode.js 20 bundled on runnerHost runner shell (bash, pwsh, etc.)Custom Linux image (Dockerfile / registry)
Dependency ManagementPre-bundled via @vercel/nccUses tools pre-installed on runnerPackaged inside the container filesystem
Nested Actions SupportNo (Uses Octokit / Node libraries)Yes (Can execute uses: steps)No (Executes container entrypoint)
Lifecycle Hooks (pre/post)Yes (pre, main, post)NoYes (pre-entrypoint, post-entrypoint)
Primary Use CasesAPI integrations, PR bots, fast logicMulti-step workflow scripts, reusable setupsHeavy CLI toolchains, C/Rust, custom OS packages

5. Referencing Custom Actions in Workflows

Workflows invoke custom actions using the uses: keyword. Actions can be consumed from external repositories or from within the local repository.

Referencing Remote Actions

steps:
  # Pinned to a major release tag
  - name: Run Security Audit
    uses: enterprise-org/security-linter@v2
    with:
      scan-mode: 'deep'

  # Pinned to an immutable commit SHA (Recommended for security)
  - name: Run Build Action
    uses: enterprise-org/builder-action@8a7b9c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b

  # Stored in a repository subdirectory
  - name: Run Sub-Action
    uses: enterprise-org/monorepo/actions/setup-db@v1

Referencing Local Actions

When an action is authored inside the same repository as the workflow (often in .github/actions/), use relative directory paths starting with ./:

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      # 1. You MUST check out the repository first so local files exist on the runner
      - name: Checkout Repository Code
        uses: actions/checkout@v4

      # 2. Invoke local action using relative path
      - name: Execute In-Tree Linter
        uses: ./.github/actions/custom-linter
        with:
          target-path: './src'

[!CAUTION] The Local Action Checkout Requirement: When using uses: ./.github/actions/my-action, the runner looks for the action definition on the runner's local filesystem at $GITHUB_WORKSPACE/.github/actions/my-action. If you do not run actions/checkout before calling a local action, the directory does not exist, and the step fails immediately with Action directory does not exist.

Test Your Knowledge

An action maintainer needs to deprecate an optional input named auth-token in favor of a new input named api-key. How should the action.yml manifest be structured so that workflows passing auth-token receive a warning annotation in the workflow logs without breaking the build?

A
B
C
D
Test Your Knowledge

A DevOps team is developing a custom action that must execute on Linux, macOS, and Windows GitHub-hosted runners with minimal startup latency (< 1 second). The action executes complex business logic, calls the GitHub REST API, and parses JSON payloads. Which action type should the team select?

A
B
C
D
Test Your Knowledge

A workflow file contains a step invoking a local custom action: uses: ./.github/actions/setup-toolchain. When the workflow runs, the step fails immediately with an error indicating that the action path cannot be found. What is the most likely cause of this failure?

A
B
C
D