Push, wait for GitHub, watch it fail, fix, push again — that loop is slow.
act runs your existing GitHub Actions
workflows on your own machine inside Docker, using the same
.github/workflows/*.yml files — no extra config, no duplicated scripts.
Get it green locally, then push.
This is the plain, standard tool most developers use. There is nothing to customize:
actreads the workflow you already have.
act runs each job in a container, so Docker must be installed and running:
docker --version # must succeed
docker ps # daemon must be upIf Docker isn't installed, see https://docs.docker.com/engine/install/.
Pick whichever fits your setup — they all install the same tool:
# a) One-line install script (Linux/macOS) → installs to ./bin or a path you give
curl -sSL https://raw.githubusercontent.com/nektos/act/master/install.sh | bash -s -- -b ~/.local/bin
# b) As a GitHub CLI extension (if you already have `gh`)
gh extension install nektos/gh-act # then run it as: gh act ...
# c) Package managers
brew install act # macOS / Linuxbrew
# Arch: sudo pacman -S act
# Windows (choco): choco install act-cliVerify:
act --versionThe first time you run act, it asks which runner image to use. Pick
Medium — it's the standard catthehacker/ubuntu image that has git, curl,
build-essential, etc. (The choice is saved to ~/.actrc.)
? Please choose the default image you want to use with act:
- Large (~17GB, closest to GitHub's runner)
> Medium (~500MB, has the common tools) ← choose this
- Micro (~200MB, node only)
act -l # list the jobs act found in .github/workflows
act # run the default event (push) — runs all matching jobs
act -j build # run ONE job by id (recommended)
act pull_request # simulate a pull_request event instead of push
act -n # dry run: print the plan, execute nothing
act -v # verbose, when something misbehavesThe CI here builds inside Docker (docker-build.yml, job id build,
runner ubuntu-24.04):
act -j buildact automatically shares your host's Docker daemon with the job, so the
workflow's own docker build ... steps work without extra flags.
If act -j build prints this and does nothing:
[ubuntu-24.04/build] 🚧 Skipping unsupported platform -- Try running with `-P ubuntu-24.04=...`
it means your act version has no default Docker image for the exact
runner label this workflow requests (runs-on: ubuntu-24.04). Out of the box
act only auto-maps ubuntu-latest, ubuntu-22.04, and ubuntu-20.04, so
the ubuntu-24.04 job is skipped. Tell act which image to use with -P:
act -j build -P ubuntu-24.04=catthehacker/ubuntu:act-24.04-P maps a runner label → Docker image.
catthehacker/ubuntu:act-*
is the community image set act already uses by default for the other labels;
it ships with git, node, curl, etc., so actions/checkout and
actions/upload-artifact work. Any image works here, but a bare ubuntu:24.04
is not enough — it lacks node/git and actions/checkout would fail. Use
the catthehacker image (or the explicit ghcr.io/catthehacker/ubuntu:act-24.04).
Make it permanent so plain act -j build works — add the mapping to ~/.actrc:
echo '-P ubuntu-24.04=catthehacker/ubuntu:act-24.04' >> ~/.actrcact -j build # 1. run CI locally
# ...fix anything that fails, repeat until green...
git add -A && git commit -m "..."
git push # 2. push only once it passed locallyThat's the whole idea — no scripts, no hooks required.
If you want the check to run on its own, add a one-line Git pre-push hook.
This is optional; act on its own is already enough.
cat > .git/hooks/pre-push <<'EOF'
#!/usr/bin/env bash
act -j build || { echo "❌ local CI failed — push aborted"; exit 1; }
EOF
chmod +x .git/hooks/pre-pushBypass it any time with git push --no-verify.
act≈ GitHub, not identical. It usescatthehackerimages, not GitHub's exact runner image. Close enough to catch the vast majority of failures; for maximum parity choose the Large image.- Linux jobs only.
actcannot runwindows-latestormacos-latestjobs. This repo'swindows-build.ymltherefore cannot run underact— only the Linuxdocker-build.ymljob does. - First run is slow — it pulls the runner image (and here, builds the project's Docker image). Later runs reuse cached layers and are much faster.
- Secrets: pass them with
act -s NAME=valueor--secret-fileif a workflow needs them (this repo's build doesn't).
docker ps # Docker running?
act -l # what jobs exist?
act -j build # run CI locally → green? then git push