Auto-Removing Containers After Workflow Execution with `act --rm`
The act --rm command-line flag automatically deletes Docker containers and their attached volumes immediately after a GitHub Actions workflow finishes, preventing disk space exhaustion from accumulated stopped containers.
The open-source CLI tool act (nektos/act) executes GitHub Actions workflows locally by spawning Docker containers for each job. Without explicit cleanup, these containers persist on your host as stopped instances, cluttering the Docker daemon and consuming storage. The --rm flag triggers a robust cleanup mechanism that removes containers regardless of whether the workflow succeeds or fails.
How act --rm Works Internally
The flag operates through a multi-layered pipeline that spans command-line parsing, configuration propagation, and Docker runtime integration.
Flag Registration in cmd/root.go
At line 95 of cmd/root.go, the Cobra CLI framework registers the --rm boolean flag. When parsed, its value is stored in the input.autoRemove field:
// Simplified representation of the registration
cmd.PersistentFlags().BoolVarP(&input.autoRemove, "rm", "", false, "automatically remove the container after execution")
Propagation to Runner Configuration
At line 637 in the same file (cmd/root.go), the autoRemove value is copied into the runtime configuration struct as rc.Config.AutoRemove. This makes the setting available to the job executor and container runtime throughout the lifecycle of the workflow.
Docker CLI Integration
In pkg/container/docker_cli.go at line 205, the boolean translates directly into Docker’s native --rm option. When constructing the docker run command, the code injects copts.autoRemove, instructing the Docker daemon to delete the container automatically upon exit:
// From pkg/container/docker_cli.go
if c.autoRemove {
args = append(args, "--rm")
}
Fallback Cleanup in job_executor.go
Even if Docker’s native --rm fails to trigger (for example, when a job errors before the container fully starts), the pkg/runner/job_executor.go file implements a manual safety net at lines 107-108. The cleanup logic evaluates the condition rc.Config.AutoRemove || jobError == nil, meaning:
- If
--rmis set, the container is removed regardless of job status. - If
--rmis omitted, the container is removed only if the job succeeded (jobError == nil).
This ensures containers never leak when the explicit removal flag is active.
Usage Examples
Basic Auto-Removal
Run a specific job and automatically clean up the container afterward, even if steps fail:
act --rm -j build
Combining with Other Flags
Pull fresh images, bind-mount the working directory, and enable auto-removal in a single command:
act --pull --bind --rm -j test
Debugging Container Lifecycle
Verify that --rm actually deletes containers by comparing docker ps output:
# First run without --rm to observe the leftover container
act -j lint
docker ps -a # Shows a stopped container named "act-<uuid>"
# Now run with --rm
act --rm -j lint
docker ps -a # The previous container is gone; no new stopped instances appear
Key Source Files
Understanding the implementation requires examining these specific locations in the nektos/act repository:
cmd/root.go(lines 95, 637): Defines the global--rmflag and propagatesinput.autoRemoveintorc.Config.AutoRemove.pkg/container/docker_cli.go(line 205): Appends the--rmoption to the Docker run command whenc.autoRemoveis true.pkg/runner/runner.go(lines 22-23): Contains theConfigstruct definition, including theAutoRemoveboolean field that controls cleanup behavior.pkg/runner/job_executor.go(lines 107-108): Houses the fallback cleanup logic that removes containers whenAutoRemoveis enabled or when jobs exit without error.
Summary
act --rmregisters a boolean flag incmd/root.gothat flows intorc.Config.AutoRemove.- The flag injects Docker’s native
--rmoption viapkg/container/docker_cli.go, ensuring automatic deletion on container exit. - A fallback mechanism in
pkg/runner/job_executor.goguarantees container removal even when Docker’s auto-remove cannot execute, such as during early job failures. - When
--rmis omitted, containers persist after runs unless the job succeeds, while the--reuseflag intentionally retains containers for caching purposes.
Frequently Asked Questions
Does act --rm remove containers even when workflow jobs fail?
Yes. According to the source code in pkg/runner/job_executor.go, the cleanup condition rc.Config.AutoRemove || jobError == nil evaluates to true whenever the --rm flag is set, ensuring removal regardless of whether jobError is nil or contains an error from a failed step.
How does --rm differ from the --reuse flag?
The --reuse flag is designed to keep containers alive between runs to cache the GitHub Actions environment and dependencies, speeding up subsequent executions. In contrast, --rm explicitly destroys the container immediately after the job finishes, reclaiming disk space but requiring fresh container creation on the next run.
Will --rm delete Docker volumes created during the workflow?
Yes. When Docker’s native --rm flag is passed (via pkg/container/docker_cli.go), the Docker daemon automatically removes any anonymous volumes attached to the container. However, named volumes declared in your workflow may persist depending on your Docker daemon configuration.
Where can I find the configuration struct that stores the --rm setting?
The AutoRemove field is defined in pkg/runner/runner.go at lines 22-23 within the Config struct. This field is populated from the command-line input in cmd/root.go and is referenced by both the Docker container logic and the job executor to determine cleanup behavior.
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 →