Forum Discussion

semod92's avatar
semod92
Occasional Reader
Nov 04, 2026

Hardening Azure Container Registry: Soft Delete & Retention Policies

Hi everyone,

We are planning to enable soft delete on our Azure Container Registries (ACR), set to the maximum retention of 90 days. In addition, we want to introduce a retention policy for images and artifacts within the repositories, so that old or orphaned images with potential security vulnerabilities can no longer find their way into circulation.

Ideally, we would also like to validate the whole setup safely via a dry run before applying it to production registries. Before we roll this out, I would like to share our approach and get feedback from the community.

Goals

  • Protect against accidental deletion (restore window of up to 90 days)
  • Automatically clean up stale, untagged or orphaned images and artifacts
  • Reduce the attack surface caused by outdated images with known CVEs
  • Keep storage costs and registry sprawl under control
  • Test every cleanup rule risk-free before enforcing it

Step 1: Enable soft delete (90 days)

az acr config soft-delete update \
  --registry <registryName> \
  --status enabled \
  --days 90

Deleted manifests and tags can be listed and restored within the retention window:

az acr manifest list-deleted --registry <registryName> --name <repo>
az acr manifest restore --registry <registryName> --name <repo>:<tag>

Step 2: Retention policy for untagged manifests

az acr config retention update \
  --registry <registryName> \
  --status enabled \
  --days 30 \
  --type UntaggedManifests

Step 3: Scheduled purge for old tagged images (dry run first)

The built-in retention policy only covers untagged manifests. For old tagged images, acr purge via an ACR Task is the right tool, and it supports --dry-run:

az acr run --registry <registryName> \
  --cmd "acr purge --filter '<repo>:.*' --ago 60d --keep 5 --untagged --dry-run" \
  /dev/null

Review the output, and only when you are happy, remove --dry-run and schedule the command as a recurring ACR Task (az acr task create --schedule "0 2 * * Sun").

Best practices we are considering

  • Test with dry run first: run acr purge --dry-run against non-production registries and review the list of affected images before enforcing anything.
  • Always use --keep: retain the last N tags per repository so rollbacks stay possible.
  • Protect important tags: use --filter patterns to exclude release/production tags, or lock critical images with az acr repository update --write-enabled false.
  • Combine with vulnerability scanning: use Microsoft Defender for Containers to identify vulnerable images and drive purge criteria, rather than relying on age alone.
  • Align retention with your deployment cadence: make sure no running workload (AKS, Container Apps, App Service) still references an image that would be purged.
  • Use consistent tagging: avoid reusing latest; use immutable, versioned tags (e.g. semantic version plus commit SHA).
  • Roll out via IaC: manage the settings through Bicep or Terraform and Azure Policy so every new registry gets the same baseline.
  • Monitor and audit: send ACR diagnostic logs to Log Analytics and alert on unexpected delete operations.
  • Mind the cost: soft-deleted data still counts toward storage until the retention window expires.

Open questions for the community

  • Are you running soft delete with 90 days in production? Any pitfalls?
  • How do you combine retention policies with acr purge in practice?
  • Do you use vulnerability findings (e.g. from Defender) as a trigger for image cleanup?
  • Are there known limitations (SKU, geo-replication, preview status) we should consider?

Looking forward to your experiences and feedback!

2 Replies

  • Your recovery and cleanup goals make sense, but Steps 1 and 2 cannot be enabled together: ACR disallows combining soft delete with the untagged-manifest retention policy.

     

    Soft delete remains preview, supports all SKUs and 1–90 days, and excludes registries with geo-replication or artifact cache. Soft-deleted artifacts continue incurring storage charges. Untagged retention is a separate Premium preview feature; its deletion is irreversible and excludes existing untagged manifests predating activation and OCI manifests.

     

    Choose the policy model first, then test the exact purge command with --dry-run in a nonproduction registry. Review tags and digests against deployed workloads before enforcement; age and --keep do not establish that an image is unused or vulnerability-free. Use Defender findings to prioritize remediation rather than treating age as a CVE detector. Validate deletion and restoration with disposable images before production; a purge dry run alone does not validate the complete recovery design.