How do reusable workflows differ from composite actions in GitHub Actions, and when should you choose one over the other in a large monorepo?
Quick Answer
Reusable workflows run as separate workflow jobs with their own runner context and full workflow features, while composite actions run inline within a calling job's step. Choose reusable workflows for orchestrating multi-job pipelines and composite actions for packaging reusable step-level logic.
Detailed Answer
Think of reusable workflows and composite actions like the difference between hiring a subcontractor versus using a power tool. A subcontractor (reusable workflow) brings their own workspace, tools, and schedule—they operate independently and report back results. A power tool (composite action) works in your hands, on your workbench, extending what you can do within your own workspace. Both save effort, but they solve fundamentally different problems.
Reusable workflows are standalone workflow files stored in a repository's .github/workflows directory that other workflows can call using the 'uses' keyword at the job level. They execute as entirely separate jobs with their own runner, environment, secrets context, and permissions. This means they can define multiple jobs, use strategy matrices, reference environment protection rules, and manage their own concurrency groups. They accept inputs and secrets through a well-defined interface using 'workflow_call' trigger, and they can output values back to the caller.
Composite actions, on the other hand, are defined in an action.yml file and execute as a sequence of steps within the calling job's runner. They share the runner's filesystem, environment variables, and tool cache with the steps around them. Composite actions can use 'runs', 'shell', and 'using: composite' to bundle multiple run and uses steps together. They cannot define jobs, use strategy matrices, or reference environments. However, they have direct access to the calling job's workspace, making file sharing trivial.
In production at scale, teams typically use reusable workflows for standardizing CI/CD pipelines across an organization—for example, a central platform team might maintain deploy-to-eks.yml or run-security-scans.yml that all product teams call. A monorepo like payments-api with 30 microservices benefits from reusable workflows because each service can call the same deployment pipeline while maintaining isolation. Composite actions shine for packaging common step sequences like setting up authentication, configuring cloud SDKs, or running linting with specific configurations that multiple jobs within the same workflow need.
A critical gotcha is the four-level nesting limit: reusable workflows can call other reusable workflows up to four levels deep, but composite actions cannot call reusable workflows. Secret passing is another pain point—reusable workflows require explicit secret forwarding (or inherit via 'secrets: inherit'), while composite actions automatically access the calling job's secrets context. Also, reusable workflows from private repositories can only be called by workflows in the same organization, and they count against the 20 unique reusable workflow references per workflow file limit. Debugging is harder with reusable workflows since logs appear as a separate job, whereas composite action steps appear inline with your other steps.