Running GitHub Actions Locally with act: 5x Faster Development
GitHub Actions are powerful for automating bioinformatics pipelines, but waiting 5-10 minutes for each cloud run is painful during development. act lets you run GitHub Actions workflows locally on your machine in seconds, slashing feedback time by 5x.
In this post, we'll explore act, a command-line tool that runs GitHub Actions locally using Docker. Perfect for testing ML pipelines, gene expression analysis, and CI/CD workflows before pushing to GitHub.
Why Test GitHub Actions Locally?
Traditional GitHub Actions workflow:
- Write workflow → Push to GitHub
- Wait 5-10 minutes for cloud runner
- Workflow fails → Fix locally → Push again
- Repeat steps 2-3 (multiple times!)
Total feedback cycle: 30+ minutes for a simple fix
With act:
- Write workflow
- Run locally with
act→ Instant feedback (10-30 seconds) - Debug and iterate locally
- Push confident code to GitHub
Total feedback cycle: 5 minutes
Part 1: Installation and Setup
Step 1: Install act with Pixi (Recommended)
The easiest way to install act globally is using Pixi, a fast package manager for Python and system tools:
Install act globally with Pixi:
pixi global install act
That's it! Pixi handles downloading the correct binary for your OS (macOS, Linux, or Windows).
Verify Installation
act --version
# Output: act version 0.2.X
Why use Pixi?
- ✅ Cross-platform (macOS, Linux, Windows)
- ✅ No system dependencies needed
- ✅ One command:
pixi global install act - ✅ Automatic updates:
pixi global upgrade act - ✅ Isolated from system Python/packages
- ✅ Same tool you use for project management
Alternative Installation Methods
If you prefer not to use Pixi, here are other options:
macOS (Homebrew)
brew install act
Linux
# Debian/Ubuntu
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | bash
# Or with pacman (Arch)
pacman -S act
Windows (PowerShell)
choco install act
# Or if using Scoop:
scoop install act
Manual Installation
- Download from GitHub Releases
- Extract to a directory in your
$PATH
Step 2: Install Docker
act requires Docker to run workflows in containers (just like GitHub's cloud runners).
Option 1: Docker Desktop (Easiest)
- Download Docker Desktop for macOS or Windows
- Install and start the application
- Docker will automatically be available in your terminal
Option 2: Install Docker with Pixi (if you prefer package managers)
# Install Docker tools in your Pixi environment
pixi global install docker
Option 3: Linux (System Package)
# Debian/Ubuntu
sudo apt-get install docker.io docker-compose
sudo usermod -aG docker $USER # Add current user to docker group
newgrp docker # Activate group changes
Verify Docker Installation
docker --version
docker run hello-world # Should complete successfully
Troubleshooting Docker:
- macOS/Windows: Ensure Docker Desktop is running (check system tray icon)
- Linux: Check if docker daemon is running:
sudo systemctl start docker - Permission denied: Add user to docker group:
sudo usermod -aG docker $USER
Step 3: Verify Setup
Test that act can find your GitHub workflows:
cd /path/to/your/repo
act --list
Expected output:
Stage Job ID Job Name Workflow Name Workflow File Events
0 test test test-workflow .github/workflows/test.yml push
If no workflows appear, ensure .github/workflows/ exists with .yml files.
Step 4: Pixi Integration (Optional but Recommended)
If your project uses Pixi, add act as a task for easy execution:
Add to pixi.toml:
[tasks]
ci = "act push"
ci-test = "act pull_request"
ci-debug = "act -v push"
Then run with Pixi:
pixi run ci # Run GitHub Actions locally
pixi run ci-debug # With verbose output
Check your Pixi setup:
pixi info
# Should show: act is available globally
Part 2: Basic Usage - Running Workflows Locally
Simple Example: Python Test Workflow
Let's create a minimal GitHub Actions workflow and test it with act.
Create .github/workflows/test.yml:
name: Python Tests
on:
push:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install pytest numpy pandas
- name: Run tests
run: |
pytest tests/ -v
Run locally with act:
act push
Expected output:
[Python Tests/test] 🚀 Start image pull...
[Python Tests/test] 🐳 Docker pull requested ghcr.io/catthehacker/ubuntu:act-latest
[Python Tests/test] ✓ Image pull complete
[Python Tests/test] 🚀 Start container...
[Python Tests/test] ⭐ Run Main actions/checkout@v3
[Python Tests/test] ✓ Complete job
Running Specific Workflows
List all available workflows:
act --list
Run a specific job:
act --job test
Run a specific workflow:
act --workflow test.yml
Simulate a different event (e.g., pull_request):
act pull_request
Part 3: Environment Variables and Secrets
Passing Environment Variables
act provides several ways to pass variables:
Method 1: Command-line flag
act -e event.json
Where event.json contains:
{
"repository": {
"name": "my-repo",
"owner": {
"login": "myusername"
}
}
}
Method 2: .actrc file (in repo root)
# .actrc
-P ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest
-l
Method 3: Shell environment variables
export MY_VAR="value"
export ANOTHER_VAR="another"
act push
Working with Secrets
GitHub Actions use secrets for sensitive data. With act, you can pass secrets locally:
Method 1: .secrets file (in repo root)
# .secrets
MY_SECRET=super_secret_value
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
API_KEY=your_api_key_here
Important: Add .secrets to .gitignore to prevent committing secrets!
echo ".secrets" >> .gitignore
Method 2: Command-line secret flag
act -s MY_SECRET=value -s GITHUB_TOKEN=token
Workflow using secrets:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use secret
run: |
echo "API Key: ${{ secrets.API_KEY }}"
echo "Token: ${{ secrets.GITHUB_TOKEN }}"
Run with secrets:
act --secret-file .secrets
Part 4: Docker and Container Management
Understanding act Container Images
act uses pre-built Docker images that mimic GitHub's cloud runners. Default images are large (~15GB) but highly compatible.
Available Docker images:
# Ubuntu (recommended for bioinformatics)
ghcr.io/catthehacker/ubuntu:act-latest
# Debian (smaller, faster)
ghcr.io/catthehacker/ubuntu:full-latest
# Minimal (smallest, fast)
ubuntu:latest # Docker Hub
Specifying Docker Images
Method 1: -P flag in command
act -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:full-latest
Method 2: .actrc file
# .actrc
-P ubuntu-latest=ghcr.io/catthehacker/ubuntu:full-latest
-P windows-latest=ghcr.io/catthehacker/windows:full-latest
Method 3: Command-line shorthand
# Use minimal image
act --container-architecture linux/amd64
Pre-pulling Docker Images
Large images take time on first run. Pre-pull them:
# Pull the image once
docker pull ghcr.io/catthehacker/ubuntu:act-latest
# Now act will use cached image (much faster)
act push
Managing Disk Space
Docker images consume significant space. Clean up unused images:
# Remove unused images
docker image prune -a
# Remove all containers
docker container prune -a
# Check disk usage
docker system df
Part 5: Performance Optimization Tips
1. Use Smaller Docker Images
Instead of full Ubuntu, use minimal images:
Before (slow - ~15GB):
act -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest
After (fast - ~2GB):
act -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:full-latest
Performance impact: ~2-3x faster
2. Cache Dependencies
GitHub Actions support caching. Leverage it in act:
Workflow with caching:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache pip dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest tests/
Performance impact: First run ~30s, subsequent runs ~5s (cache hit)
3. Run Jobs in Parallel
By default, act runs jobs sequentially. Enable parallel execution:
# Run all jobs in parallel
act --parallel 4
Workflow with multiple jobs:
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- run: pytest tests/unit/
integration-tests:
runs-on: ubuntu-latest
steps:
- run: pytest tests/integration/
lint:
runs-on: ubuntu-latest
steps:
- run: pylint src/
Sequential time: 30s + 40s + 10s = 80s Parallel time (--parallel 3): ~40s
4. Use act -l (List) Mode
For quick workflow checks without running:
act --list
Shows all jobs without executing them.
5. Rebuild Docker Image
Cache can become stale. Rebuild:
act --reuse-containers # Reuse running containers
act --rebuild # Rebuild image from scratch
Real Example: Bioinformatics Gene Expression Pipeline
Let's build a complete ML pipeline workflow and test it locally with act.
Workflow File: .github/workflows/ml-pipeline.yml
name: ML Pipeline - Gene Expression Analysis
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip packages
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements.txt') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov pytest-xdist
- name: Lint with pylint
run: pylint src/ --exit-zero
continue-on-error: true
- name: Run unit tests
run: pytest tests/unit/ -v --cov=src --cov-report=xml
- name: Run integration tests
run: pytest tests/integration/ -v
- name: Upload coverage reports
uses: codecov/codecov-action@v3
if: matrix.python-version == '3.12'
with:
files: ./coverage.xml
flags: unittests
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Bandit security check
run: |
pip install bandit
bandit -r src/ -v --exit-code 0
continue-on-error: true
Running Locally with act
Run all jobs:
act push
Expected output:
[ML Pipeline - Gene Expression Analysis/test] 🚀 Start image pull...
[ML Pipeline - Gene Expression Analysis/test] ⭐ Run actions/checkout@v3
[ML Pipeline - Gene Expression Analysis/test] ✓ Step 'Checkout code' completed
[ML Pipeline - Gene Expression Analysis/test] ⭐ Run Set up Python 3.10
[ML Pipeline - Gene Expression Analysis/test] ✓ Step 'Set up Python 3.10' completed
[ML Pipeline - Gene Expression Analysis/test] ⭐ Run Install dependencies
[ML Pipeline - Gene Expression Analysis/test] ✓ Step 'Install dependencies' completed
[ML Pipeline - Gene Expression Analysis/test] ⭐ Run Run unit tests
...
[ML Pipeline - Gene Expression Analysis/test] ✓ Complete job
[ML Pipeline - Gene Expression Analysis/security] ⭐ Run Bandit security check
[ML Pipeline - Gene Expression Analysis/security] ✓ Complete job
Run specific Python version:
act push --job test --matrix python-version=3.12
Run only security tests:
act push --job security
Debugging Workflows with act
Enable Verbose Logging
act -v push
Shows detailed output including Docker commands and environment variables.
Debug with Interactive Shell
If a step fails, enter the container:
# Create a failing step
steps:
- run: |
echo "Debug info:"
env | sort
ls -la
Or use act with shell access:
act -b # Use local container (not rebuilding)
Inspect Container After Failure
Keep container running after failure:
# View logs
docker ps -a
# Enter the container
docker exec -it <container_id> /bin/bash
Common Issues and Solutions
Issue: "Cannot connect to Docker daemon"
# Solution: Start Docker
sudo systemctl start docker # Linux
open /Applications/Docker.app # macOS
Issue: "Not enough space for Docker images"
# Solution: Clean up
docker system prune -a --volumes
Issue: "Workflow runs but tests fail locally but pass on GitHub"
# Solution: Check Python versions match
python --version
act --list # Verify Python version in workflow
Performance Comparison: Local vs Cloud
| Task | Local (act) | GitHub Cloud | Speedup |
|---|---|---|---|
| First run | 45s | 300s+ | 6-7x |
| Subsequent runs (cached) | 8s | 250s+ | 30x |
| Development iteration | 2 min (10 runs) | 50 min | 25x |
| Cost | $0 | $0.008/min | N/A |
Key insight: For a typical development session with 10 iterations, act saves 48 minutes of waiting time!
Best Practices for CI/CD with act
1. Test Locally Before Pushing
# Before git push
act push
# If all pass, then:
git push origin main
2. Match GitHub Runner Environment
# Use same image as GitHub
act -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest
3. Commit .actrc to Repository
# .actrc (can be committed)
-P ubuntu-latest=ghcr.io/catthehacker/ubuntu:full-latest
-l
But keep .secrets in .gitignore:
# .gitignore
.secrets
4. Use Matrix Strategy for Multiple Versions
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
os: [ubuntu-latest, macos-latest]
Test all combinations locally before push.
5. Document Dependencies
# requirements.txt
pytest>=7.0.0
numpy>=1.24.0
pandas>=1.5.0
scipy>=1.10.0
Keep updated so act can replicate exact environment.
6. Use Pixi for Global Tools (Bioinformatics Recommended)
If you use Pixi for project management, keep your global tools there too:
# Install act globally with Pixi
pixi global install act
# Update act easily
pixi global upgrade act
# See all globally installed tools
pixi global list
Benefits for bioinformatics workflows:
- Single package manager for all tools
- Easy version control (
pixi.lock) - Reproducible environments across team
- No system package conflicts
Create a .pixi.toml for your project:
[dependencies]
python = "3.12"
pytest = ">=7.0.0"
numpy = ">=1.24.0"
pandas = ">=1.5.0"
[tasks]
test = "pytest tests/ -v"
lint = "pylint src/"
ci = "act push" # Run GitHub Actions locally
Then your team can just run:
pixi run test # Run tests
pixi run lint # Run linter
pixi run ci # Run GitHub Actions locally
Key Takeaways
actruns GitHub Actions locally → test before pushing to GitHub- 5-30x faster feedback → iterate quickly during development
- Docker-based → identical environment to GitHub Cloud runners
- Supports secrets and environment variables → test real workflows
- Performance optimization → use smaller images, caching, parallel jobs
- Free and open-source → no additional costs beyond your machine
What's Next?
Now that you can test workflows locally with act, consider:
- Setting up pre-commit hooks to run
actautomatically - Creating reusable workflow templates for bioinformatics pipelines
- Integrating
actinto your team's development process - Exploring GitHub Actions Marketplace for bioinformatics tools
References
- act GitHub Repository
- GitHub Actions Documentation
- Docker Documentation
- GitHub Actions Best Practices
Happy local testing! 🚀