Automates cleanup of old container images from DigitalOcean registries based on retention policies. Keeps last 10 tags for staging, 30 for production, always preserves :latest.
Purpose: Clean up old container images to manage storage costs in DigitalOcean Container Registries.
When to Use:
When NOT to Use:
This skill is the ONLY registry cleanup procedure.
DO NOT duplicate registry cleanup automation in:
| Repository | Retention | Always Keep | Purpose |
|---|---|---|---|
witchcityrope-api-staging |
10 tags | :latest |
Staging API images |
witchcityrope-web-staging |
10 tags | :latest |
Staging Web images |
witchcityrope-api-production |
30 tags | :latest |
Production API images |
witchcityrope-web-production |
30 tags | :latest |
Production Web images |
| Repository | Retention | Always Keep | Purpose |
|---|---|---|---|
accounting-api |
30 tags | :latest |
Production API (treat as production) |
accounting-web |
30 tags | :latest |
Production Web (treat as production) |
Rationale:
Executable Script: execute.sh
# From project root - shows what WOULD be deleted
bash .claude/skills/registry-cleanup/execute.sh
Output: Shows repositories, tag counts, tags that would be deleted, estimated storage savings.
NO CHANGES MADE - Safe to run anytime.
# From project root - ACTUALLY DELETES old tags
bash .claude/skills/registry-cleanup/execute.sh --confirm
Output: Shows repositories, deletes old tags, reports actual deletions, storage freed.
DESTRUCTIVE - Tags deleted cannot be recovered.
curl and jq installed:latest tag (always preserved)--confirm flag usedbash execute.shbash execute.sh --confirm# Example: Save audit log
bash .claude/skills/registry-cleanup/execute.sh --confirm 2>&1 | tee registry-cleanup-$(date +%Y-%m-%d).log
๐งน Registry Cleanup - DRY RUN MODE
===================================
๐ Analyzing WitchCityRope Registry
-----------------------------------
Repository: witchcityrope-api-staging
Total tags: 25
Retention policy: 10 most recent
Tags to delete: 15
Keeping (10 newest + :latest):
โ
sha-abc123f (2025-11-22)
โ
sha-def456g (2025-11-21)
... (8 more)
โ
latest (protected)
Would delete (15 oldest):
๐๏ธ sha-xyz789h (2025-10-15)
๐๏ธ sha-uvw456i (2025-10-10)
... (13 more)
Estimated storage freed: ~350 MB
Repository: witchcityrope-web-staging
Total tags: 18
Retention policy: 10 most recent
Tags to delete: 8
Would delete (8 oldest):
๐๏ธ sha-rst234j (2025-10-18)
... (7 more)
Estimated storage freed: ~200 MB
๐ Summary
----------
Total repositories: 6
Total tags: 125
Tags to keep: 72
Tags to delete: 53
Estimated storage freed: ~850 MB
โ ๏ธ DRY RUN - No changes made
โน๏ธ Run with --confirm to actually delete tags
๐งน Registry Cleanup - CONFIRM MODE
===================================
โ ๏ธ WARNING: This will DELETE old tags permanently!
... (same analysis as dry-run) ...
๐๏ธ Deleting tags from witchcityrope-api-staging...
Deleted: sha-xyz789h โ
Deleted: sha-uvw456i โ
... (13 more)
โ
Deleted 15 tags from witchcityrope-api-staging
... (repeat for other repositories) ...
๐ Summary
----------
Total repositories: 6
Total tags deleted: 53
Actual storage freed: ~850 MB
โ
Registry cleanup complete
:latest and :sha-XXXXXX tags:latest and :sha-XXXXXX tags:latestRecommended cron schedule:
# Every Sunday at 2 AM
0 2 * * 0 cd /home/chad/repos/witchcityrope && bash .claude/skills/registry-cleanup/execute.sh --confirm 2>&1 | tee -a /var/log/registry-cleanup.log
Monitor storage costs in DigitalOcean dashboard:
Error: User secrets not found or empty
Solution:
cd /home/chad/repos/witchcityrope/apps/api
dotnet user-secrets list | grep DigitalOcean:Token
If empty, set token:
dotnet user-secrets set "DigitalOcean:Token" "YOUR_TOKEN_HERE"
Error: 401 Unauthorized
Solution: Token may be expired or invalid. Generate new token:
Error: 404 Not Found or 500 Server Error
Solution:
Symptom: Tags deleted but storage usage unchanged
Explanation: DigitalOcean processes deletions asynchronously. Wait 1-2 hours for storage metrics to update.
Edit execute.sh to modify retention counts:
# Repository configurations (line ~80)
declare -A RETENTION
RETENTION["witchcityrope-api-staging"]=10 # Change to 5, 15, etc.
RETENTION["witchcityrope-web-staging"]=10
RETENTION["witchcityrope-api-production"]=30 # Change to 20, 50, etc.
RETENTION["witchcityrope-web-production"]=30
Consider:
Edit execute.sh to add new repositories:
# Repository configurations (line ~80)
RETENTION["new-service-staging"]=10
RETENTION["new-service-production"]=30
Skill automatically processes all configured repositories.
List Repository Tags:
curl -X GET \
-H "Authorization: Bearer $TOKEN" \
"https://api.digitalocean.com/v2/registry/witchcityrope/repositories/witchcityrope-api-staging/tags"
Delete Tag:
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
"https://api.digitalocean.com/v2/registry/witchcityrope/repositories/witchcityrope-api-staging/tags/sha-abc123f"
Response Codes:
200 OK - List successful204 No Content - Delete successful401 Unauthorized - Invalid token404 Not Found - Repository/tag not found429 Too Many Requests - Rate limited (wait 60 seconds)For questions or issues: Consult DigitalOcean Container Registry documentation or raise issue in repository.