Skip to content

Testing GitHub Actions Workflows Locally with act

This guide shows you how to test GitHub Actions workflows locally using act, dramatically speeding up workflow development and debugging.

What is act?

act is an open-source tool (by nektos) that runs your GitHub Actions workflows locally using Docker. Instead of pushing code to GitHub and waiting for Actions to run, you can test workflows on your machine in seconds.

Benefits

  • Speed: Test workflows in 5-20 seconds vs 2-5 minutes on GitHub
  • Cost: Save GitHub Actions minutes (especially on private repos)
  • Iteration: Debug faster without polluting git history
  • Offline: Test without internet connection (after images downloaded)

Limitations

  • Some GitHub-specific features may not work perfectly
  • Requires Docker installed and running
  • Large workflows may be slow on first run (image pull)

Installation

Linux/macOS

curl --proto '=https' --tlsv1.2 -sSf \
  https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash

macOS (Homebrew)

brew install act

Windows (Chocolatey)

choco install act-cli

Verify Installation

act --version

Configuration

First Run Setup

On first run, act will ask you to choose a Docker image size:

? Please choose the default image you want to use with act:

- Large size image: +17GB Docker image, includes almost all tools
- Medium size image: ~500MB, includes only necessary tools  ← Recommended
- Micro size image: <200MB, contains only NodeJS required to bootstrap

Recommendation: Choose Medium image for balance of features and size.

Configuration File

Create .actrc in your project root:

# .actrc
# Use medium-sized image
-P ubuntu-latest=catthehacker/ubuntu:act-latest

# Specify GitHub token for private repos
-s GITHUB_TOKEN=<your-token>

# Specify AWS credentials for Lambda testing
-s AWS_ACCESS_KEY_ID=<your-access-key>
-s AWS_SECRET_ACCESS_KEY=<your-secret-key>

# Use specific platforms
--container-architecture linux/amd64

# Verbose output for debugging
-v

Secrets File

Create .secrets file (add to .gitignore!):

# .secrets
GITHUB_TOKEN=ghp_your_token_here
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
GITOPS_PAT=ghp_another_token_here
SYRF_GITHUB_APP_ID=123456
SYRF_GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"

Important: Add .secrets and .actrc to .gitignore:

echo ".secrets" >> .gitignore
echo ".actrc" >> .gitignore

Basic Usage

List Available Workflows

# List all workflows
act -l

# Example output:
# Stage  Job ID              Job name             Workflow name        Workflow file
# 0      detect-changes      Detect Changed...    CI/CD Pipeline       ci-cd-refactored.yml
# 1      version             Version Services     CI/CD Pipeline       ci-cd-refactored.yml
# 2      build-docker        Build Docker...      CI/CD Pipeline       ci-cd-refactored.yml

Run Specific Workflow

# Run default trigger (usually 'push')
act

# Run specific event
act push
act pull_request
act workflow_dispatch

# Run specific workflow file
act -W .github/workflows/test-ci-cd.yml

# Run specific job
act -j detect-changes
act -j version
act -j build-docker

Dry Run (See What Would Run)

# Show what would run without executing
act -n

# Show more detailed graph
act -n --graph

Testing Our Refactored Workflow

Test Change Detection

# Test detect-changes job only
act push -j detect-changes

# With specific file changes (simulated)
act push -j detect-changes \
  --env GITHUB_EVENT_PATH=<(echo '{"commits":[{"modified":["src/services/api/README.md"]}]}')

Test Version Calculation

# Test version job (requires GitVersion)
act push -j version

# Note: May fail if GitVersion not in Docker image
# Solution: Use larger image or custom Dockerfile

Test Single Service Build

# Test API build only
# First, comment out other services in matrix
act push -j build-docker

Test with Specific Inputs

For workflow_dispatch workflows:

# Test with specific scenario
act workflow_dispatch \
  -W .github/workflows/test-ci-cd.yml \
  --input test_scenario=single-service-api \
  --input simulate_build_failure=none

Test Reusable Workflows

Reusable workflows are tricky in act. Best approach:

# Create a test caller workflow
cat > .github/workflows/test-reusable.yml << 'EOF'
name: Test Reusable
on: push
jobs:
  test:
    uses: ./.github/workflows/reusable-gitversion.yml
    with:
      service_name: api
      config_path: src/services/api/GitVersion.yml
EOF

# Run it
act push -W .github/workflows/test-reusable.yml

# Clean up
rm .github/workflows/test-reusable.yml

Advanced Usage

Use Custom Docker Image

If you need specific tools (GitVersion, AWS CLI, etc.):

# .act/Dockerfile
FROM catthehacker/ubuntu:act-latest

# Install GitVersion
RUN curl -sL https://github.com/GitTools/GitVersion/releases/download/6.0.0/gitversion-linux-x64-6.0.0.tar.gz | tar xz -C /usr/local/bin
RUN chmod +x /usr/local/bin/gitversion

# Install AWS CLI
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
    && unzip awscliv2.zip \
    && ./aws/install \
    && rm -rf awscliv2.zip aws

Build and use:

# Build custom image
docker build -t act-custom .act/

# Use it
act -P ubuntu-latest=act-custom

Environment Variables

# Set environment variables
act push \
  --env MY_VAR=value \
  --env ANOTHER_VAR=another_value

# Load from file
act push --env-file .env.test

Bind Volumes

# Mount local directory into container
act push \
  --bind /path/on/host:/path/in/container

Debug Failed Workflows

# Verbose output
act push -v

# Very verbose (includes Docker commands)
act push -vv

# Even more verbose
act push -vvv

Interactive Debugging

# Drop into shell on failure
act push --shell

# Manually execute steps in the container
docker exec -it <container-id> bash

Workflow-Specific Testing

Test Change Detection Logic

Create test event files:

# Test API change
cat > /tmp/api-change.json << EOF
{
  "commits": [
    {
      "added": [],
      "modified": ["src/services/api/SyRF.API.Endpoint/Program.cs"],
      "removed": []
    }
  ]
}
EOF

act push -j detect-changes \
  --eventpath /tmp/api-change.json

Test Matrix Builds

# Test matrix expansion
act push -j build-docker -n

# See what combinations would run
act push -j build-docker --matrix service:api
act push -j build-docker --matrix service:pm

Test Conditional Logic

# Test with specific conditions
act push -j promote-to-staging \
  --env needs.build-docker.outputs.api_success=true \
  --env needs.build-docker.outputs.pm_success=false

Troubleshooting

Issue: "DOCKER_HOST not set"

Solution:

# Linux
export DOCKER_HOST=unix:///var/run/docker.sock

# macOS
export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock

Issue: "Permission denied" errors

Solution:

# Add user to docker group
sudo usermod -aG docker $USER

# Logout and login again

Issue: Workflow uses GitHub-specific features

Solution: Use --matrix or --job to run only testable parts:

# Skip jobs that require GitHub features
act push -j detect-changes -j version

# Or use event files to mock GitHub context

Issue: Secrets not working

Solution: Verify secrets file:

# Check secrets are loaded
act -l --secret-file .secrets -v

Issue: Workflow too slow

Solution:

  1. Use smaller Docker images
  2. Run specific jobs only
  3. Cache Docker layers
  4. Use local Docker registry

Best Practices

1. Use .actrc for Configuration

Don't pass flags every time:

# .actrc
-P ubuntu-latest=catthehacker/ubuntu:act-latest
--secret-file .secrets
-v

2. Test Incrementally

Don't run entire workflow every time:

# Test jobs in order
act push -j detect-changes
act push -j version
act push -j build-docker

3. Use Dry Run First

Always check what will run:

act push -n

4. Mock External Dependencies

For jobs that call external APIs:

# Create mock responses
act push -j deploy-lambda \
  --env AWS_DEFAULT_REGION=eu-west-1 \
  --bind /tmp/mock-aws:/root/.aws

5. Clean Up After Testing

# Remove stopped containers
docker ps -a --filter "name=act-" --filter "status=exited" -q | xargs docker rm

# Remove dangling images
docker image prune -f

Integration with Development Workflow

Pre-Push Hook

Add to .git/hooks/pre-push:

#!/bin/bash
echo "Testing workflows locally before push..."

# Test validation
if ! act -W .github/workflows/validate-workflows.yml; then
  echo "❌ Workflow validation failed"
  exit 1
fi

echo "✅ Workflow validation passed"

VS Code Task

Add to .vscode/tasks.json:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Act: Test Workflows",
      "type": "shell",
      "command": "act push -n",
      "group": "test",
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    },
    {
      "label": "Act: Run Change Detection",
      "type": "shell",
      "command": "act push -j detect-changes",
      "group": "test"
    }
  ]
}

Makefile

Add to Makefile:

.PHONY: test-workflows
test-workflows:
 @echo "Testing workflows with act..."
 act push -n

.PHONY: test-detect-changes
test-detect-changes:
 act push -j detect-changes

.PHONY: test-version
test-version:
 act push -j version

Comparison: act vs GitHub Actions

Feature act (Local) GitHub Actions
Speed 5-20 seconds 2-5 minutes
Cost Free (local compute) Limited minutes/month
Network Can work offline Requires internet
Accuracy ~90% compatible 100% accurate
Debugging Interactive shell access Log-based only
Secrets Local file GitHub Secrets
Cache Docker cache GitHub cache
Parallelization Limited by local resources GitHub infrastructure

When to Use act vs GitHub

Use act for

  • ✅ Rapid iteration during development
  • ✅ Syntax checking and validation
  • ✅ Testing workflow logic
  • ✅ Debugging complex workflows
  • ✅ Pre-push verification

Use GitHub Actions for

  • ✅ Final validation before merge
  • ✅ Production deployments
  • ✅ Integration with GitHub features
  • ✅ Testing on different OS/architectures
  • ✅ Long-running workflows

Resources

Next Steps

  1. Install act on your development machine
  2. Create .actrc and .secrets files
  3. Test the refactored workflow locally
  4. Integrate act into your development workflow
  5. Share act setup with team members