Bulk Delete GitHub Actions Workflow Runs¶
GitHub Actions accumulates workflow run history quickly. After a few months of active development, a busy repository can have thousands of runs: every push, every pull request, every scheduled job. Most of those runs are no longer useful, but the GitHub web interface only lets you delete one run at a time. If you want to clean up a large backlog, you need a different approach.
This post covers a one-liner that bulk-deletes all workflow runs for a repository using the GitHub CLI (gh) and jq.
Why You Might Want to Do This¶
A few common reasons to clear out old workflow run history:
- A past run captured a secret or sensitive value in its logs, and you want to remove it.
- A repository was renamed, transferred, or repurposed, and the old run history is noise.
- You are tidying up before open-sourcing a project that previously had a private CI history.
- You simply want a clean slate after a period of heavy churn.
GitHub does not offer a bulk-delete option in the UI, and the REST API only supports deleting one run per request. Scripting it is the practical solution.
Prerequisites¶
You need two tools installed and available on your PATH.
Install the GitHub CLI¶
After installation, authenticate with GitHub:
Run gh auth status to confirm your session is active before continuing.
Install jq¶
The Script¶
REPO="OWNER/REPO"
LIMIT=1000
gh run list --repo "$REPO" --limit "$LIMIT" --json databaseId | \
jq -r '.[].databaseId' | \
while read run_id; do
echo "Deleting workflow run: $run_id"
gh run delete "$run_id" --repo "$REPO" <<< "y"
done
Set REPO to the full repository path, for example REPO="tenthirtyam/setup-task". Adjust LIMIT as needed (the maximum accepted by gh run list is 1000).
How It Works¶
gh run list --repo "$REPO" --limit "$LIMIT" --json databaseIdfetches up to$LIMITworkflow runs for the repository, returning only thedatabaseIdfield for each run.jq -r '.[].databaseId'extracts those IDs as plain text, one per line.- The
while readloop iterates over each ID and callsgh run deleteto remove it. The<<< "y"automatically confirms the deletion prompt so the loop runs unattended.
Handling More Than 1,000 Runs¶
The --limit flag on gh run list caps at 1,000. If the repository has more runs than that, run the script multiple times until the history is empty, or adjust the loop to run until gh run list returns an empty result:
REPO="OWNER/REPO"
LIMIT=1000
while true; do
ids=$(gh run list --repo "$REPO" --limit "$LIMIT" --json databaseId | jq -r '.[].databaseId')
[ -z "$ids" ] && break
echo "$ids" | while read run_id; do
echo "Deleting workflow run: $run_id"
gh run delete "$run_id" --repo "$REPO" <<< "y"
done
done
Targeting a Specific Workflow¶
To delete runs for a single workflow rather than the entire repository, add the --workflow flag with the workflow file name:
REPO="OWNER/REPO"
WORKFLOW="ci.yml"
LIMIT=1000
gh run list --repo "$REPO" --workflow "$WORKFLOW" --limit "$LIMIT" --json databaseId | \
jq -r '.[].databaseId' | \
while read run_id; do
echo "Deleting workflow run: $run_id"
gh run delete "$run_id" --repo "$REPO" <<< "y"
done
A Note on Permissions¶
Deleting workflow runs requires write access to the repository. If you are running this against an organization repository, confirm that your token or GitHub App has the actions:write permission before running the script.