Production engineering guide for github actions advanced covering patterns, implementation strategies, and operational best practices.
This reference covers complex patterns and failure modes in GitHub Actions workflows, focusing on execution behavior, artifact handling, and configuration nuances that affect production reliability. These patterns matter when building workflows that must handle edge cases, maintain consistency across environments, or integrate with external systems.
GitHub Actions uses if expressions in step definitions to control execution flow. The expression syntax supports logical operators and built-in functions like github.event, job.status, and steps.
- name: Deploy to staging
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
echo "Deploying to staging environment"
deploy-staging.sh
- name: Run tests only on pull requests
if: github.event_name == 'pull_request'
run: |
echo "Running tests for PR #${{ github.event.pull_request.number }}"
run-tests.sh
The if condition evaluates in the context of the current step. A common failure occurs when using github.ref instead of github.base_ref or github.head_ref. The expression github.ref == 'refs/heads/main' fails for pull request merges because github.ref refers to the target branch, not the source.
Jobs in a workflow can depend on each other using the needs field. The dependency system evaluates job status, but it's important to understand that needs waits for all dependencies to complete successfully, not just start.
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Building"
test:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "Testing"
deploy:
needs: [build, test]
runs-on: ubuntu-latest
steps:
- run: echo "Deploying"
If test fails, deploy will not run even though it's configured to depend on both jobs. The workflow will show deploy as skipped rather than failed.
The matrix strategy creates parallel jobs with different parameter combinations. When using multiple dimensions, the system generates a Cartesian product of all values.
strategy:
matrix:
node-version: [14.x, 16.x]
os: [ubuntu-latest, windows-latest]
include:
- os: ubuntu-latest
node-version: 18.x
Each combination runs as a separate job. The system treats the final matrix as a single job that fails if any matrix job fails. Matrix jobs do not share environment variables unless explicitly configured.
Artifacts can be uploaded from workflow steps using the actions/upload-artifact action. The maximum artifact size is 10 GB per upload, with a total limit of 50 GB per workflow run.
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-output
path: |
dist/
build.log
retention-days: 7
The path parameter accepts multiple patterns, each on a separate line. The upload process fails if the total artifact size exceeds limits, but the failure is silent at the upload step level.
Downloading artifacts uses the actions/download-artifact action with pattern matching.
- name: Download specific files
uses: actions/download-artifact@v3
with:
name: build-output
path: ./output/
The download preserves directory structure but does not preserve file permissions or extended attributes. A common failure mode occurs when artifacts are uploaded from one platform and downloaded on another, leading to permission mismatches.
Artifacts can be configured to expire after a set number of days using retention-days. This setting applies to all artifacts in a workflow run, not individual uploads.
- name: Upload with retention
uses: actions/upload-artifact@v3
with:
name: logs
path: logs/
retention-days: 30
If retention-days is not set, the default is 90 days. Artifacts are deleted automatically after the retention period, but this deletion is not logged in workflow output.
Environment variables can be set in multiple ways: at the job level, step level, or globally for a workflow. The env key at the workflow level sets variables for all jobs.
env:
NODE_ENV: production
API_URL: https://api.example.com
jobs:
deploy:
runs-on: ubuntu-latest
env:
DEPLOY_ENV: staging
steps:
- run: echo $NODE_ENV
- run: echo $DEPLOY_ENV
Variables set in env at the workflow level are available to all jobs, while job-level variables override workflow-level ones. The system evaluates variable references at runtime, not at parse time.
Secrets are configured in the GitHub repository settings under "Secrets and variables" > "Actions". Secrets are encrypted using GitHub's encryption keys before storage and decrypted at runtime.
- name: Deploy with secrets
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/deploy
Secrets are not available in the workflow YAML itself. They must be referenced using ${{ secrets.SECRET_NAME }}. If a secret is referenced but not defined, the step fails with an error like Error: Secret 'SECRET_NAME' not found.
Environments can have protection rules that control when workflows can deploy to them.
jobs:
deploy:
environment:
name: production
url: https://app.example.com
The workflow waits for environment approval before running. If no approval is configured, the deployment proceeds immediately. Environment URLs are displayed in workflow logs but do not affect execution behavior.
Workflows can be triggered by custom events using workflow_dispatch or by webhook payloads.
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
default: 'staging'
The workflow_dispatch event allows manual triggering of workflows with parameters. The system validates input types and requires defined defaults for optional parameters.
Scheduled workflows use cron expressions to trigger at specific times.
on:
schedule:
- cron: '0 2 * * *'
The cron expression runs in UTC timezone, not the repository's local time zone. The system evaluates the expression against UTC time. If a scheduled workflow fails, it does not retry automatically.
Workflows can be triggered by various repository events including pushes, pull requests, and issues.
on:
push:
branches: [ main ]
paths: [ 'src/**' ]
pull_request:
branches: [ main ]
types: [ opened, synchronize ]
The paths filter works on the changed files, not the complete repository state. If a file is modified but not included in the path filter, the workflow does not trigger even if other files change.
Jobs can specify runner labels to target specific infrastructure.
jobs:
build:
runs-on: [self-hosted, linux, x64]
steps:
- run: echo "Running on custom runner"
The system selects runners based on label matching. If no runner matches the specified labels, the job waits for a matching runner or fails if none is available.
Job timeouts can be configured using the timeout-minutes setting.
jobs:
long-running:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- run: |
echo "This job will timeout after 60 minutes"
sleep 3600
The timeout applies to the entire job execution, not individual steps. If a job times out, it is marked as failed but does not retry automatically.
Steps can specify resource requirements using with parameters.
- name: Run with resources
uses: actions/checkout@v3
with:
fetch-depth: 0
The fetch-depth parameter controls how many commits are fetched during checkout. Setting it to 0 fetches all commits, which is useful for Git history operations.
Steps that fail cause the workflow to stop unless explicitly configured otherwise.
- name: Run with error handling
continue-on-error: true
run: |
echo "This step can fail"
exit 1
The continue-on-error flag allows a workflow to continue even if a step fails. If multiple steps fail, the overall job status is failed.
Workflow failures are detected by step exit codes and error messages. The system treats non-zero exit codes as failures.
- name: Fail gracefully
run: |
echo "Processing failed"
exit 1
The workflow logs show detailed error information including stack traces and environment state. Errors during artifact upload do not cause the overall workflow to fail unless explicitly configured.
Debugging workflows can be enabled using environment variables.
- name: Enable debug
run: |
echo "DEBUG mode enabled"
ls -la
The system sets several built-in variables including GITHUB_WORKFLOW, GITHUB_RUN_ID, and GITHUB_ACTOR. These are available in all steps and can be used for logging or conditional behavior.
GitHub Actions has specific resource constraints that affect workflow execution. Each job runs with limited CPU and memory resources, and the system enforces maximum run times.
jobs:
memory-intensive:
runs-on: ubuntu-latest
steps:
- run: |
# This operation might exceed available memory
echo "Memory intensive task"
Jobs running on shared runners have limited resources compared to self-hosted runners. The system does not provide detailed resource usage information in logs.
Workflows can be configured to retry failed steps or jobs.
- name: Retryable step
uses: actions/checkout@v3
with:
fetch-depth: 0
retry: 2
The retry logic applies only to the specific action, not the entire workflow. If a workflow fails due to external conditions, it may need manual intervention.
Workflow performance can be improved by reducing step count and optimizing artifact usage.
- name: Combined steps
run: |
echo "Step 1"
echo "Step 2"
echo "Step 3"
Combining multiple operations in a single step reduces overhead. Large artifacts cause longer upload/download times, affecting workflow duration.
This page was rewritten on 15 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy