ghcr-cleanup-manager
v1.1.6
Published
GHCR Cleanup Manager: GitHub Action and CLI for safe GitHub Container Registry cleanup and inspection.
Maintainers
Readme
GHCR Cleanup Manager
GHCR Cleanup Manager is a GHCR cleanup action for GitHub Container Registry packages.
GHCR Cleanup Manager is a GitHub Action for:
- clean GHCR packages including tagged and untagged images
- preview cleanup with
dry-runbefore making changes - scan GHCR packages into SQLite database artifacts
- visualize GHCR package graphs and their changes with the visualizer
It is built for safe GHCR cleanup on OCI package graphs, including multi-arch images, attestations, cosign signatures, and other referrers that simpler GHCR cleanup actions often mishandle.
Example graph compare view: red-bordered manifests are present in the older scan and removed in the newer one.
Quick Start
For a first run, start with cleanup in dry-run mode.
jobs:
cleanup:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
actions: write
concurrency:
group: ghcr-cleanup-manager__OWNER__PACKAGE
steps:
- uses: actions/checkout@v6
- name: Preview GHCR cleanup
id: ghcr-cleanup-manager
uses: ghcr-manager/[email protected]
with:
command: cleanup
token: ${{ github.token }}
owner: OWNER
package: PACKAGE
dry-run: true
delete-untagged: true
keep-n-tagged: 10
exclude-tags: |
latest
upload-artifacts: truePermission notes:
scanandcleanupdry-runs needpackages: read- live
cleanupthat mutates GHCR needspackages: write- artifact upload needs
actions: write
After the run:
- Open the GitHub step summary for the action run.
- Review which tags matched and which roots would be deleted, untagged, or blocked.
- Only download the DB artifact if you need deeper inspection.
Commands
The action supports two commands:
cleanup: Cleans using filters; usedry-runto preview the resultscan: Scans one package and uploads the resulting DB artifact
Purpose of commands
cleanup: Normal entry point for registry maintenancescan: For investigation and audit
Common Usage
Preview cleanup
- uses: ghcr-manager/[email protected]
with:
command: cleanup
token: ${{ github.token }}
owner: OWNER
package: PACKAGE
dry-run: true
delete-tags: |
pr-.*
use-regex: true
older-than: 30 days
keep-n-tagged: 5
exclude-tags: |
latest
stable
upload-artifacts: trueApply cleanup
- uses: ghcr-manager/[email protected]
with:
command: cleanup
token: ${{ github.token }}
owner: OWNER
package: PACKAGE
delete-untagged: true
keep-n-tagged: 10
upload-artifacts: true
scan-after-cleanup: trueIf scan-after-cleanup is true, cleanup performs a second scan so the uploaded DB reflects post-mutation state.
Note: the second scan only runs if cleanup actually makes changes.
Live cleanup permission note: change the workflow permission from packages: read to packages: write before turning
off dry-run.
Scan one package
- uses: ghcr-manager/[email protected]
with:
command: scan
token: ${{ github.token }}
owner: OWNER
package: PACKAGEscan always uploads a DB artifact.
Inputs
| Input | Description | Cmds | Required | Default |
| ---------------------------- | ----------------------------------- | ---- | -------- | ------------------------------ |
| command | scan or cleanup | all | Yes | |
| token | GitHub token for API calls | all | Yes | ${{ github.token }} |
| owner | Package owner | all | Yes | |
| package | Package name | all | Yes | |
| db-path | Local SQLite DB path | s,c | No | |
| upload-artifacts | Upload DB and summary artifacts | s,c | No | false |
| scan-after-cleanup | Run a second scan after cleanup | c | No | false |
| db-artifact-retention-days | Override artifact retention days | s,c | No | ${{ github.retention_days }} |
| delete-tags | Newline-separated tags to delete | c | No | |
| exclude-tags | Newline-separated tags to exclude | c | No | |
| keep-n-tagged | Keep newest tagged roots | c | No | |
| keep-n-untagged | Keep newest untagged roots | c | No | |
| delete-untagged | Delete untagged roots | c | No | false |
| delete-ghost-images | Delete ghost multi-arch roots | c | No | false |
| delete-partial-images | Delete partial multi-arch roots | c | No | false |
| delete-orphaned-images | Delete orphaned digest-derived tags | c | No | false |
| older-than | Age cutoff for cleanup selectors | c | No | |
| use-regex | Use regex for cleanup tag selectors | c | No | false |
| dry-run | Show changes without mutating GHCR | c | No | false |
| log-level | CLI log level | all | No | info |
Cmds: s = scan, c = cleanup
Cleanup command notes:
- Tagged selector families may be combined with
delete-untagged. exclude-tagsrequires at least one tagged selector family.delete-untaggedandkeep-n-untaggedcannot be combined.older-thantakes one integer plus one unit.- Supported
older-thanunits:minutes,hours,days,weeks,months,years. - Example values:
30 days,2 hours,1 month.
- Supported
Outputs
| Output | Description |
| ------------------- | ------------------------------------ |
| db-path | SQLite DB path on the runner |
| summary-json-path | Summary JSON file path for cleanup |
Artifacts
When artifacts are enabled:
scanalways uploads one SQLite DB artifactcleanupoptionally uploads the DB artifact and a cleanup summary JSON artifact
Current naming:
| Artifact type | Filename pattern |
| -------------------- | --------------------------------------------------- |
| scan or cleanup DB | ${OWNER}__${PACKAGE}.sqlite |
| cleanup summary JSON | ${OWNER}__${PACKAGE}.sqlite--cleanup-summary.json |
Documentation Map
- GitHub Action usage: action
commands, including
cleanupandscan - Cleanup behavior: how cleanup protects retained tags and handles shared graphs
- ghcr-cleanup-manager-visualizer: local graph inspection and scan-to-scan comparison
- Multi-package workflows: cleaning up multiple packages with one combined DB
- SQLite schema guide: practical explanation of the SQLite schema
- CLI usage: companion CLI usage
Explore A Real Scenario DB
The release assets also include one merged SQLite DB from GHCR Cleanup Manager's live scenario workflows. You can use it as a quick visualizer demo and as a compact way to inspect dozens of real cleanup and graph cases.
curl -LO https://github.com/ghcr-manager/ghcr-cleanup-manager/releases/latest/download/ghcr-cleanup-manager-release-scenarios.sqlite
npx ghcr-cleanup-manager-visualizer --db ./ghcr-cleanup-manager-release-scenarios.sqliteDocker image available: visualizer Docker usage.
For a first look in the visualizer, start with:
- owner:
ghcr-cleanup-manager-test - package: select one with
2imagesor2multiarchin the name - tag search:
imageormultiarch
For more details, see visualizer and test-scenarios.
Cleanup Behavior
Cleanup is tag-based for both selection and protection. Use tags to say what should be cleaned up, and use retained tags to keep image and multi-arch graphs protected.
Manifests reachable from retained tags stay protected. Manifests no longer reachable from any retained tag may be removed during cleanup. If you pull images by digest, make sure those digests are still reachable through tags that your cleanup rules keep.
For the full explanation and graph examples, see cleanup-behavior.
Project
Main project and issue tracker:
- Repository: https://github.com/ghcr-manager/ghcr-cleanup-manager
- Issues: https://github.com/ghcr-manager/ghcr-cleanup-manager/issues
Similar Tools
- ghcr-manager/ghcr-untag-action: focused tag removal without the broader scan and cleanup workflow.
- dataaxiom/ghcr-cleanup-action: another GHCR cleanup action with a similar problem focus.
- mkoepf/ghcrctl: CLI tooling for working with GHCR packages and graph deletions.

