Reduce GCP storage costs by automatically cleaning up stale container images.
A policy-driven container image lifecycle manager for Google Artifact Registry (GAR). Replaces the deprecated gcr.io shell script with a production-grade Go binary that runs as a scheduled Cloud Run Job — define retention rules in YAML, and let the tool handle the rest.
- Policy-as-code — retention rules in
policy.yaml, reviewed via pull request - Dry-run by default — see what would be deleted before committing
- Kubernetes guard — never deletes images in use by running workloads (phase 2)
- BigQuery audit trail — every deletion recorded for cost attribution (phase 4)
- Semver protection —
v1.2.3-style tags are never deleted unless explicitly allowed - Terraform IaC — Cloud Run Job + Scheduler + BigQuery table ready to deploy
| Manual cleanup | Deprecated gcr.io scripts |
gar-cleanup | |
|---|---|---|---|
| Automated | No | Partial | Yes — scheduled Cloud Run Job |
| Policy-driven | No | No | Yes — YAML retention rules |
| Kubernetes-aware | No | No | Yes — protects in-use images |
| Auditable | No | No | Yes — BigQuery audit trail |
| Actively maintained | N/A | Deprecated | Yes |
- Cost Optimization — automatically delete stale images to reduce GAR storage spend
- Repository Hygiene — remove untagged and orphaned digests on a schedule
- Compliance & Audit — every deletion logged to BigQuery for cost attribution and reporting
- Kubernetes Safety — images referenced by running Pods and ReplicaSets are never deleted
1. Define policy ──▶ policy.yaml (retention rules, tag protection, dry-run toggle)
2. Schedule run ──▶ Cloud Run Job triggered daily by Cloud Scheduler
3. Images cleaned ──▶ Stale images deleted safely; audit events streamed to BigQuery
# Build
make build
# Dry-run against your project (reads policy.yaml)
./bin/gar-cleanup run \
--project my-gcp-project \
--location us-central1 \
--policy policy.yaml
# Live deletion (requires dry_run: false in policy.yaml)
./bin/gar-cleanup run \
--project my-gcp-project \
--liveEdit policy.yaml to configure retention rules:
dry_run: true # set false only after reviewing the dry-run log
keep: 10 # retain the 10 most-recently pushed images per repo
max_age: 30d # delete images older than 30 days
protect_tags: # these tags are NEVER deleted
- "^v\\d+\\.\\d+\\.\\d+$" # semver releases
- "^stable$"
- "^production$"
delete_untagged: true # delete digests with no tags immediatelyPolicy changes must go through a pull request — the policy is applied on the next scheduled run.
Requirements: Go 1.24+
# Build
make build
# Run tests
make test
# Coverage report
make coverInfrastructure is managed with Terraform:
cd terraform
terraform init
terraform apply -var="project_id=my-gcp-project"This creates:
- A
gar-cleanupservice account with least-privilege IAM bindings - A Cloud Run Job that executes the cleanup binary
- A Cloud Scheduler job that triggers it daily at 02:00 UTC
- A BigQuery table for audit events
See ARCHITECTURE.md for full design documentation.
| Command | Description |
|---|---|
gar-cleanup run |
Execute a cleanup run (dry-run by default) |
gar-cleanup version |
Print version, commit, and build date |
| Flag | Default | Description |
|---|---|---|
--policy |
policy.yaml |
Path to YAML policy file |
--project |
$GOOGLE_CLOUD_PROJECT |
GCP project ID |
--location |
us-central1 |
GAR location |
--live |
false |
Execute live deletions |
See ARCHITECTURE.md for the full design, component map, and delivery phases.
See CONTRIBUTING.md.
To report a vulnerability, see .github/SECURITY.md. Do not open a public issue.
Apache 2.0 — see LICENSE.
Related topics: container image lifecycle, Google Artifact Registry cleanup, GCP cost optimization, Kubernetes image garbage collection, container retention policy, Cloud Run scheduled jobs, DevOps automation, SRE tooling, infrastructure as code, policy-as-code