Binding Work Directories in Act Using `--bind`: A Complete Technical Guide
The --bind (or -b) flag tells act to mount your host working directory directly into the job container instead of copying files, eliminating the copy step and enabling instant file synchronization between host and container.
The nektos/act tool executes GitHub Actions workflows locally inside Docker containers. By default, the runner copies your entire repository into a Docker volume before starting each job, which creates overhead for large codebases or file-watching workflows. The --bind flag changes this behavior to create a live bind mount, drastically improving startup times and allowing real-time edits to reflect immediately inside running containers.
How --bind Works Under the Hood
When you enable the bind mode, act bypasses its default copy mechanism and constructs a direct filesystem bridge between your host and the job container. This involves three key stages across the codebase:
CLI Flag Definition and Configuration
The flag is declared in cmd/root.go and stored in the runner configuration. When parsing arguments, act sets the Config.BindWorkdir boolean field defined in pkg/runner/runner.go. This single boolean determines whether the runner will copy files or mount them.
Bind Mount String Construction
The core logic resides in RunContext.GetBindsAndMounts() inside pkg/runner/run_context.go. When BindWorkdir is true, the function constructs a Docker bind string using the following pattern:
fmt.Sprintf("%s:%s%s", rc.Config.Workdir, ext.ToContainerPath(rc.Config.Workdir), bindModifiers)
The bindModifiers string varies by host operating system to optimize performance and security:
- macOS: Appends
:delegatedto improve filesystem performance on Docker Desktop - SELinux-enabled hosts: Appends
:zto relabel the mount context for proper access control
Container Launch and Docker Integration
After constructing the bind list, RunContext.startJobContainer() (also in pkg/runner/run_context.go) passes the configuration to container.NewContainer. The actual Docker API call occurs in pkg/container/docker_cli.go, where the bind string populates the Binds field of the container's host configuration. Docker then mounts the host directory directly into the container's workspace path, creating a live two-way sync.
Using --bind in Practice
Enable bind mounting with the short or long flag form when invoking act:
Run a specific job with bind mounting for faster startup:
act -b -j build
Explicitly disable the Docker socket bind while using workspace binding:
act -b --container-daemon-socket -
Combine bind mode with a custom runner image:
act -b -P ubuntu-latest=catthehacker/ubuntu:act-latest
Without the -b flag, act creates a named Docker volume for the workspace and copies the repository contents into it before execution begins.
Default Behavior Without --bind
When --bind is omitted, GetBindsAndMounts() creates a named Docker volume instead of a bind mount. The code assigns the container path to a volume map entry:
mounts[name] = ext.ToContainerPath(rc.Config.Workdir)
act then performs a copy operation to populate this volume with your repository files. While this provides better isolation and avoids potential file permission issues, it introduces latency during job startup and requires explicit re-runs to see host filesystem changes.
Summary
--bindmounts the host working directory directly into the job container, while the default mode copies files into a named Docker volume.- The flag sets
Config.BindWorkdirinpkg/runner/runner.go, which triggers bind string construction inRunContext.GetBindsAndMounts(). - Bind modifiers (
:delegatedfor macOS,:zfor SELinux) are automatically appended based on the host platform. - Use
act -boract --bindto enable live file editing and faster job startup, especially for large repositories or workflows that watch files.
Frequently Asked Questions
Does --bind improve performance for all projects?
Yes, particularly for large repositories and workflows with file watchers. Eliminating the copy step reduces job startup time significantly when your repository contains many files or large assets. However, for very small projects, the performance difference may be negligible compared to the default volume-based approach.
How does act handle file permissions with bind mounts on Linux?
On SELinux-enabled systems, act automatically appends the :z flag to the bind mount string. This relabels the mounted files so the container process can access them. If you encounter permission errors inside the container when using --bind, verify that your user has read access to the files on the host, as the container inherits the same UID/GID constraints.
Can I use --bind with remote Docker hosts?
The bind mount points to your local filesystem path. If you are using a remote Docker daemon (via DOCKER_HOST), the remote daemon will attempt to mount a path from the remote machine's filesystem, not your local machine. In this scenario, the default copy behavior or volume mounts are more appropriate unless your remote Docker context has access to the same filesystem paths.
What happens if I edit files while a job is running with --bind?
Changes are visible immediately inside the container. Since the host directory is bind-mounted, any file modifications, deletions, or additions on your host appear instantly in the container's workspace. This is ideal for debugging workflows that depend on file watchers or for iterative development where you need to fix code and retry steps without restarting the entire job.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →