No description
  • Go 79.5%
  • Shell 14.1%
  • Go Template 5.4%
  • Dockerfile 1%
Find a file
renovate[bot] d7227a3ce8
chore: configure Renovate #1
Configure Renovate
2026-06-10 13:56:23 +04:00
.github/workflows ci: unify image and chart release workflow 2026-05-15 10:58:54 +04:00
charts/drc ci: unify image and chart release workflow 2026-05-15 10:58:54 +04:00
cmd/drc feat: add delete_empty_repos support for docker 2026-05-15 00:10:33 +04:00
internal fix: make keep optional when gc_only is enabled 2026-05-15 10:29:11 +04:00
test feat: initial release of Docker Registry Cleaner (drc) 2026-05-14 21:13:13 +04:00
.dockerignore feat: initial release of Docker Registry Cleaner (drc) 2026-05-14 21:13:13 +04:00
.gitignore ci: update release workflow with semver tags and GitHub release 2026-05-14 22:11:15 +04:00
compose.yaml feat: initial release of Docker Registry Cleaner (drc) 2026-05-14 21:13:13 +04:00
Dockerfile chore: update go to 1.26 and alpine to 3.23 2026-05-14 22:34:51 +04:00
go.mod feat: initial release of Docker Registry Cleaner (drc) 2026-05-14 21:13:13 +04:00
go.sum chore: add missing go.sum 2026-05-14 21:31:25 +04:00
README.md docs: improve clarity in GC scheduling recommendation 2026-05-23 11:05:47 +04:00
renovate.json chore: configure Renovate #1 2026-06-10 13:56:23 +04:00
Taskfile.yml feat: initial release of Docker Registry Cleaner (drc) 2026-05-14 21:13:13 +04:00

Docker Registry Cleaner

Docker Registry Cleaner helps you clean up your stale images with retention policies and use your registry garbage collection. It can be used with multiple registries. Works with Kubernetes and Docker/Podman.

Features

  • Multiple registries in a single run
  • Per-repository retention policies with tag filters
  • Garbage collection for Kubernetes and Docker/Podman
  • Dry run mode

Quick Start

# config.yaml
registries:
  - name: my-registry
    url: http://registry:5000
    keep: 5
    delete_untagged: true
    repos:
      my-app:
        keep: 10
        tag_filter: "^v\\d+\\.\\d+\\.\\d+$"
drc -c config.yaml

Configuration

Global Settings

Field Type Default Description
log_level string info Log level (debug, info, warn)
dry_run bool false Preview deletions only
concurrency int 1 Parallel registry processing
timeout duration 30s Per-registry timeout

Registry

Field Type Required Description
name string Yes Registry identifier
url string Yes Registry URL
username string No Basic auth username
password string No Basic auth password
username_env string No Env var for username
password_env string No Env var for password
keep int No* Default tags to keep per repository
delete_untagged bool No Delete manifests without tags
gc_only bool No Skip tag cleanup, only run GC

Per-Repository Overrides

Field Type Description
keep int Override default tag count
tag_filter string Regex to include matching tags only
delete_untagged bool Override untagged deletion
skip bool Skip this repository entirely

Garbage Collection

Kubernetes

Field Type Required Description
kubernetes.enabled bool Yes Enable K8s GC
kubernetes.namespace string Yes Pod namespace
kubernetes.name string No Pod name (or use label_selector)
kubernetes.label_selector string No Label selector for pod discovery
kubernetes.gc_config_path string Yes Path to registry config inside container
kubernetes.gc_delete_unreferenced_blobs bool No Delete unreferenced blobs during GC
kubernetes.delete_empty_repos bool No Delete empty repository directories via exec
kubernetes.storage_path string No Registry storage path (default: /var/lib/registry)

Docker

Field Type Required Description
docker.enabled bool Yes Enable Docker GC
docker.container string Yes Container name or ID
docker.gc_config_path string Yes Path to registry config inside container
docker.gc_delete_unreferenced_blobs bool No Delete unreferenced blobs during GC
docker.delete_empty_repos bool No Delete empty repository directories via exec
docker.storage_path string No Registry storage path (default: /var/lib/registry)

Full Example

log_level: info
dry_run: false
concurrency: 2
timeout: 60s

registries:
  - name: production
    url: https://registry.prod.example.com
    username_env: REGISTRY_USER
    password_env: REGISTRY_PASS
    keep: 5
    delete_untagged: true
    kubernetes:
      enabled: true
      namespace: registry
      label_selector: app=docker-registry
      gc_config_path: /etc/docker/registry/config.yml
      gc_delete_unreferenced_blobs: true
    repos:
      api-backend:
        keep: 10
        tag_filter: "^v\\d+\\.\\d+\\.\\d+$"
      frontend:
        skip: true
      worker:
        keep: 3

  - name: staging
    url: http://registry.staging:5000
    keep: 3
    docker:
      enabled: true
      container: registry-staging
      gc_config_path: /etc/docker/registry/config.yml

Deployment

Kubernetes (Helm)

helm repo add drc https://eznix86.github.io/docker-registry-cleaner
helm repo update

helm install drc drc/drc -n drc --create-namespace

Create a Kubernetes Secret for registry credentials instead of putting passwords in values.yaml:

kubectl create namespace drc

kubectl create secret generic registry-cleaner-credentials \
  -n drc \
  --from-literal=username='YOUR_USERNAME' \
  --from-literal=password='YOUR_PASSWORD'

Reference the Secret through environment variables in your Helm values:

config:
  log_level: info
  dry_run: true
  concurrency: 1
  timeout: 30s
  registries:
    - name: registry
      url: http://docker-registry.docker-registry.svc.cluster.local:5000
      keep: 5
      username_env: REGISTRY_USERNAME
      password_env: REGISTRY_PASSWORD
      delete_untagged: true
      kubernetes:
        enabled: true
        namespace: docker-registry
        label_selector: app.kubernetes.io/name=docker-registry
        gc_config_path: /etc/docker/registry/config.yml
        gc_delete_unreferenced_blobs: true

env:
  - name: REGISTRY_USERNAME
    valueFrom:
      secretKeyRef:
        name: registry-cleaner-credentials
        key: username
  - name: REGISTRY_PASSWORD
    valueFrom:
      secretKeyRef:
        name: registry-cleaner-credentials
        key: password

Then install or upgrade with your values file:

helm upgrade --install drc drc/drc \
  -n drc \
  --create-namespace \
  -f values.yaml

Docker Compose

services:
  drc:
    image: ghcr.io/eznix86/docker-registry-cleaner:latest
    volumes:
      - ./config.yaml:/etc/drc/config.yaml
    command: ["-c", "/etc/drc/config.yaml"]

Docker

docker run --rm \
  -v $(pwd)/config.yaml:/etc/drc/config.yaml \
  ghcr.io/eznix86/docker-registry-cleaner:latest \
  -c /etc/drc/config.yaml

Garbage Collection

DRC triggers GC by executing registry garbage-collect inside the running registry container:

Note

Normally, the registry should be stopped or put in read-only mode during GC to avoid data corruption. However, Id recommend scheduling drc in your cron job at a time when you expect there to be no image pushes, such as 3 AM.

Development

# Build
task build

# Run locally
task run

# Test against local registries
task test:setup
task test:run
task test:teardown

License

MIT