act is an open source command-line tool that reads the workflow files in .github/workflows/ and runs their jobs in Docker containers on your own machine. Instead of pushing a commit and waiting for GitHub to pick it up every time you tweak a workflow, you get feedback in seconds. In this tutorial you will install act on Ubuntu 24.04, run workflows and individual jobs, choose runner images, pass secrets, variables and inputs, share artifacts between jobs, and debug a failing job.
Prerequisites
To follow this guide you need:
- A machine running Ubuntu 24.04 LTS (a workstation or a CubePath VPS used as a build box) with at least 4 GB of RAM and 20 GB of free disk space for runner images.
- A non-root user with
sudoprivileges. - Docker Engine installed from Docker's official repository, with your user in the
dockergroup so you can rundockerwithoutsudo. - A Git repository that contains at least one workflow in
.github/workflows/.
Check that Docker works for your user before continuing:
docker run --rm hello-world
The output should start with Hello from Docker!.
Step 1 - Installing act
act is distributed as a single static binary on its GitHub releases page. Download the latest Linux archive for your architecture (use act_Linux_arm64.tar.gz on ARM servers), extract the binary and install it into /usr/local/bin:
cd /tmp
curl -fLO https://github.com/nektos/act/releases/latest/download/act_Linux_x86_64.tar.gz
tar -xzf act_Linux_x86_64.tar.gz act
sudo install -m 0755 act /usr/local/bin/act
Verify the installation:
act --version
act version 0.2.80
Your version number will be whatever is current. To upgrade later, repeat the same three commands.
Step 2 - Choosing a runner image
GitHub-hosted runners are full virtual machines with hundreds of preinstalled tools. act cannot reproduce them exactly, so it maps each runs-on label to a Docker image. The first time you run act, it asks which default image size you want:
| Size | Image for ubuntu-latest | Download size | When to use it |
|---|---|---|---|
| Micro | node:16-buster-slim | small | Only JavaScript actions and basic shell steps |
| Medium | catthehacker/ubuntu:act-latest | around 500 MB compressed | Most workflows; a good default |
| Large | catthehacker/ubuntu:full-latest | many GB | Workflows that expect the full GitHub toolset |
Change into your repository and run act once to answer the prompt. Choose Medium:
cd ~/projects/your_repo
act --list
act stores your choice in its configuration file, ~/.config/act/actrc. Look at it:
cat ~/.config/act/actrc
-P ubuntu-latest=catthehacker/ubuntu:act-latest
The file also contains similar lines for specific Ubuntu versions, such as ubuntu-22.04.
Each line is a command-line flag that act applies on every run. -P (short for --platform) maps a runs-on label to an image. You can override it for a single run, for example to try the full image:
act -P ubuntu-latest=catthehacker/ubuntu:full-latest
Step 3 - Listing and running workflows
act --list shows every job act found, with the events that trigger it:
Stage Job ID Job name Workflow name Workflow file Events
0 lint lint CI ci.yml push,pull_request
0 test test CI ci.yml push,pull_request
1 build build CI ci.yml push,pull_request
The Stage column reflects needs: dependencies: jobs in stage 1 run after stage 0 finishes.
Run everything that the push event would trigger. push is the default event, so both of these commands are equivalent:
act
act push
Each line of output is prefixed with the workflow and job name, such as [CI/test], and every job finishes with a Job succeeded or Job failed line. act exits with a non-zero status if any job failed, so you can also use it in scripts.
Run only one job, or only the workflows in one file:
act -j test
act -W .github/workflows/ci.yml
To see what would run without starting containers, use a dry run:
act -n
To simulate another event, pass its name, for example act pull_request. For events whose payload matters (a pull request's branch names, a release tag), write the payload to a JSON file and pass it with -e:
nano event.json
{
"pull_request": {
"head": { "ref": "feature-branch" },
"base": { "ref": "main" }
}
}
act pull_request -e event.json
Step 4 - Passing secrets, variables and inputs
Workflows read secrets.*, vars.* and inputs.*, which do not exist on your machine. act can provide all three.
Secrets are read from a .secrets file in the repository root, in dotenv format:
nano .secrets
REGISTRY_PASSWORD=your_registry_password
DEPLOY_KEY=your_deploy_key
Keep this file out of Git:
echo ".secrets" >> .gitignore
act loads .secrets automatically. To use another file, pass --secret-file path/to/file. For a single value, use -s NAME=value, or just -s NAME to have act prompt for it without leaving it in your shell history.
Many actions need GITHUB_TOKEN, which GitHub injects automatically but act does not. If you use the GitHub CLI, pass your own token:
act -s GITHUB_TOKEN="$(gh auth token)"
Configuration variables (vars.*) work the same way through a .vars file or --var NAME=value, and plain environment variables through .env or --env NAME=value.
For workflow_dispatch workflows with inputs, pass them with --input:
act workflow_dispatch --input environment=staging --input version=1.2.3
Step 5 - Sharing artifacts between jobs
actions/upload-artifact and actions/download-artifact need an artifact server. act includes one, but only starts it when you give it a directory. Consider this workflow:
name: Build
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mkdir -p dist && date > dist/build.txt
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
verify:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- run: cat dist/build.txt
Run it with an artifact directory:
act --artifact-server-path /tmp/act-artifacts
The verify job prints the date written by build, and the uploaded files are kept under /tmp/act-artifacts for you to inspect. To avoid typing the flag every time, add --artifact-server-path /tmp/act-artifacts as a line in ~/.config/act/actrc, or in an .actrc file in the repository root for project-specific settings.
Step 6 - Debugging a failing job
When a job fails, start with verbose output. It shows the exact commands, the environment and the expressions act evaluated:
act -j test -v 2>&1 | tee act.log
If you need to look around inside the job container, keep it after the run with --reuse:
act -j test --reuse
After the run, find the container and open a shell in it:
docker ps --filter name=act- --format '{{.Names}}'
docker exec -it container_name bash
Inside, the repository is checked out at the same path as on GitHub's runners, so you can rerun the failing command by hand. Remove the container with docker rm -f container_name when you are done.
Some steps make no sense locally, such as deploying or commenting on a pull request. act sets the environment variable ACT=true in every job, so you can skip those steps only when running under act:
- name: Deploy
if: ${{ !env.ACT }}
run: ./deploy.sh
If you develop on an ARM machine but your workflows target x86_64, force the container architecture so images and binaries match GitHub's runners (this is slower, as it uses emulation):
act --container-architecture linux/amd64
Troubleshooting
permission denied while trying to connect to the Docker daemon socket. Your user is not in the docker group. Run sudo usermod -aG docker $USER, then log out and back in.
A step fails with command not found. The runner image lacks the tool. Add a setup step (for example actions/setup-python), install the package in the workflow, or switch that label to the full image with -P.
An artifact step fails with an error about ACTIONS_RUNTIME_TOKEN. An artifact or cache action ran without an artifact server. Add --artifact-server-path.
A job that uses runs-on: macos-latest or windows-latest is skipped. act only runs Linux containers. Those jobs cannot be run locally.
The run behaves differently on GitHub. act is an approximation: services, some contexts and hosted-runner tools may differ. Treat act as a fast feedback loop and still let the real workflow run before merging.
Conclusion
You can now run GitHub Actions workflows on Ubuntu 24.04 with act, target single jobs and events, feed in secrets, variables and inputs, pass artifacts between jobs and inspect failing containers. As next steps, commit a project .actrc so your team shares the same runner images, add if: ${{ !env.ACT }} guards to deployment steps, and, if act becomes part of your daily loop, pre-pull the runner images on your build machine to make the first run faster.
