Local Execution and Orchestration of GitHub Actions via Act

The modern software development lifecycle relies heavily on Continuous Integration and Continuous Deployment (CI/CD) pipelines to ensure code quality and stability. GitHub Actions has emerged as a dominant force in this domain, providing an integrated environment for automating workflows. However, a recurring pain point for developers is the "commit-push-wait-fail" cycle. This occurs when a developer pushes code to a remote repository, waits for the cloud-based runner to trigger, and only then discovers a failure in the test suite or linting process. Such a cycle is not only tedious but can lead to cluttered commit histories filled with "fix ci" or "test 1" messages, which diminishes the professional appearance of a pull request.

The ability to execute these workflows locally transforms the developer experience from a reactive process to a proactive one. By shifting the execution of GitHub Actions from the cloud to the local workstation, developers can iterate rapidly, debug in real-time, and ensure that by the time a Pull Request is opened, the tests have already passed in an environment that mimics the production runner. This shift is primarily facilitated by tools like act, which allows the local simulation of GitHub Actions workflows using Docker containers, effectively bridging the gap between the local development environment and the remote GitHub infrastructure.

The Local Execution Ecosystem with Act

To eliminate the inefficiency of remote testing, developers employ act, a tool designed to run GitHub Actions locally. The core mechanism of act is its ability to read the .github/workflows directory and execute the defined jobs within Docker containers. Because GitHub Actions runners are essentially virtual machines or containers provided by GitHub, act replicates this by pulling Docker images that resemble the environment of the official GitHub runners.

The installation of act can be performed through various package managers, with Homebrew being a primary choice for macOS users. The requirement for Docker is absolute; without a functional Docker daemon, act cannot instantiate the containers necessary to run the workflow steps. This dependency ensures that the environment remains isolated and consistent, preventing "it works on my machine" syndrome by forcing the code to run in a containerized environment that mirrors the cloud.

Addressing Docker Daemon Connectivity Issues

A common technical hurdle when integrating local action runners is the failure to connect to the Docker daemon. Developers often encounter the specific error message Cannot connect to the Docker daemon at unix:///var/run/docker.sock. This error typically arises when the tool attempting to trigger the action—whether it be the act CLI or a VSCode/Cursor extension—cannot locate the Docker socket or lacks the permissions to access it.

In environments where Rancher is installed or where Docker is running in a non-standard context, the system may not automatically resolve the Docker host. To resolve this, the DOCKER_HOST environment variable must be explicitly defined. The solution involves dynamically fetching the Docker endpoint using the docker context inspect command.

The specific command to resolve this connectivity issue is:

DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}') act

By prefixing the act command with the DOCKER_HOST variable, the developer tells the operating system exactly where the Docker daemon is listening, thereby bypassing the default socket error and allowing the workflow to initiate.

Granular Workflow Control and Command Execution

act provides a suite of flags that allow developers to control exactly what runs and how it runs. Rather than executing an entire complex workflow—which might include time-consuming linting, security scans, and multiple test suites—developers can target a specific job. This is critical for efficiency when the developer knows exactly which part of the pipeline is failing.

The following table details the common parameters used during local execution:

Parameter Purpose Usage Context
--workflows Specifies the path to the YAML workflow file Used when multiple workflows exist in the project
--job Targets a specific job ID within the workflow Used to skip unnecessary jobs like linting
--secret-file Provides a file containing encrypted secrets Used for jobs requiring API keys or tokens
--var-file Supplies a file for environment variables Used for configuration-specific data
--input-file Passes input data to the workflow Used for workflow_dispatch events
--eventpath Defines the path to the event payload Used to simulate specific GitHub events

For a scenario where a developer needs to run only the "test" job from a backend workflow while ensuring the Docker host is correctly identified, the command would be structured as follows:

DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}') act --workflows ".github/workflows/backend.yaml" --job "test" --secret-file "" --var-file "" --input-file "" --eventpath ""

This command ensures that only the necessary logic is executed, reducing the local resource load and speeding up the feedback loop. In cases where the output is excessively large, developers often redirect the output to a file, such as log.txt, and utilize AI-powered editors like Cursor to parse the logs for critical error messages, further accelerating the debugging process.

Integration with Integrated Development Environments (IDEs)

For those who prefer a graphical user interface over the command line, the "GitHub Local Actions" extension for VSCode or Cursor provides a streamlined experience. This extension abstracts the complexity of the act CLI, allowing developers to see their detected workflows and trigger them via a UI.

However, the extension relies on the same underlying infrastructure as the CLI. If the Docker daemon is not accessible, the extension will fail with the same unix:///var/run/docker.sock error. The remedy remains the same: manually executing the command with the DOCKER_HOST variable to ensure the environment is correctly mapped. Once the connection is established, the extension allows for a more visual management of the local CI/CD pipeline, making it accessible even to those who are less comfortable with the terminal.

GitHub Runner Repository and Resource Allocation

While tools like act allow for local simulation, the official GitHub Actions runner infrastructure is maintained in a public repository. It is important to note the current status of the actions/runner repository. GitHub has announced that they are not currently accepting external contributions to this specific repository.

The strategic focus of GitHub's resources is currently directed toward areas that maximize customer success and developer productivity. While GitHub Actions remains a cornerstone of their ecosystem, the development team is allocating resources to other specific areas of Actions rather than the general runner repository.

For those seeking updates or wishing to influence the direction of the tool, GitHub has established specific channels:

  • The GitHub public roadmap serves as the primary source for feature updates and the current stage of development for new capabilities.
  • Support requests and general questions are directed to the Community Discussions area.
  • High-priority bugs are handled via Community Discussions or the official support team via https://support.github.com/contact/bug-report.
  • Security vulnerabilities must be reported following the guidelines outlined in the SECURITY.md file of the repository.

Despite the freeze on general contributions, GitHub continues to provide critical security updates and bug fixes for the project to ensure the stability of the runners used globally.

Analysis of Local vs. Remote CI Execution

The transition from purely remote CI to a hybrid local-remote model represents a significant evolution in developer productivity. When a developer relies solely on remote runners, they are subject to queue times, network latency, and the "blindness" of remote logs. This often leads to a pattern of "guess-and-check" where the developer adds console.log statements to the code, pushes the change, and waits for the remote runner to execute just to see the value of a variable.

Local execution via act eliminates this inefficiency. The impact is a drastic reduction in the time spent waiting for CI results and a cleaner version control history. By running the workflow locally, the developer can use their local debugger, inspect the container state, and verify the environment variables before the code ever leaves their machine.

The synergy between act, Docker, and the DOCKER_HOST configuration creates a robust local environment. However, it is important to recognize that local simulation is not a 100% replacement for remote CI. Cloud runners provide a clean, neutral environment that can catch issues related to cached dependencies or OS-specific quirks that might not be present in a local Docker container. Therefore, the ideal workflow involves using act for rapid iteration and final verification on the remote GitHub runner before merging.

Sources

  1. GitHub Actions Runner
  2. How to run GitHub Actions locally

Related Posts