Omni FinOps

How to clean up old EBS snapshots safely

Find EBS and Google Cloud snapshots older than 180 days, check nothing still needs them, and delete them, or archive the ones you must keep.

Mateo VargasUpdated · 5 min read
Contents
  1. What old snapshots cost, and what deleting saves
  2. Find snapshots older than 180 days
  3. Check before you delete
  4. Delete, or archive what you must keep
  5. Google Cloud snapshots
  6. Set retention so they expire on their own

In short

  • Snapshots are incremental, so deleting one frees only the blocks no other snapshot shares.
  • A snapshot behind a registered AMI can’t be deleted until the AMI is deregistered.
  • Check backup plans and retention rules before deleting anything.

Snapshots are cheap per GB, which is exactly why they pile up. A daily backup nobody set to expire, the copy taken before an upgrade two years ago, the snapshots behind an image nobody launches: each one is small, and together they become a line on the bill that only grows.

This guide shows how to find EBS and Google Cloud snapshots older than 180 days, check what still depends on them, delete or archive them, and set retention so the next batch expires on its own.

What old snapshots cost, and what deleting saves

In us-east-1, as of October 2026 (EBS pricing):

Tier Storage To restore Minimum
Standard $0.05 per GB-month Free None
Archive $0.0125 per GB-month $0.03 per GB 90 days

Snapshots are incremental. The first one of a volume stores its written blocks, and each later one stores only the blocks that changed. When you delete a snapshot, AWS frees only the blocks no other snapshot still references, so deleting a snapshot might not reduce your storage costs much. A snapshot’s size times $0.05 is the most it can save, not what it will save. The oldest snapshot of a volume whose later snapshots you also delete is where the real money is.

Find snapshots older than 180 days

Set a cutoff date, list your own snapshots older than it, and leave out the ones a registered AMI uses:

cutoff=$(date -u -d '180 days ago' +%F 2>/dev/null || date -u -v-180d +%F)
for r in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
  ami_snaps=$(aws ec2 describe-images --region "$r" --owners self --include-disabled \
    --query 'Images[].BlockDeviceMappings[].Ebs.SnapshotId' --output text \
    | tr '\t' '\n' | grep .)
  aws ec2 describe-snapshots --region "$r" --owner-ids self \
    --query "Snapshots[?StartTime<='$cutoff'].[SnapshotId,VolumeId,VolumeSize,StartTime]" \
    --output text | grep -vF -f <(printf '%s' "$ami_snaps" | grep .) | sed "s/^/$r  /"
done

The first line works with both GNU and macOS date. Each row shows the region, the snapshot, the volume it came from, its size in GiB and when it was taken.

Check before you delete

Look at one snapshot’s description and tags first:

aws ec2 describe-snapshots --region us-east-1 --snapshot-ids snap-0e5d2a7f31 \
  --query 'Snapshots[0].[Description,VolumeId,StartTime,Tags]' --output json

Then work through these:

  • Does a registered AMI use it? You can’t delete it until the AMI is deregistered. The command above already leaves those out. If the AMI itself is unused, see how to deregister unused AMIs and delete their snapshots.
  • Did AWS Backup make it? Its description says so. It belongs to a backup vault and its retention is set by the backup plan, so expire it there, not by hand.
  • Did Data Lifecycle Manager make it? It carries an aws:dlm:lifecycle-policy-id tag. The policy’s retain rule deletes it in time; if it keeps too many, change the rule.
  • Does a rule require you to keep it? Legal, audit or customer contract retention. Archive it instead of deleting it.
  • Is its source volume still alive with newer snapshots? Then a 180-day-old one rarely adds anything to recovery.

Delete, or archive what you must keep

Delete:

aws ec2 delete-snapshot --region us-east-1 --snapshot-id snap-0e5d2a7f31

If you want an undo window, create a Recycle Bin rule for snapshots in the region first. Deleted snapshots stay recoverable for the period you set:

aws rbin create-rule --region us-east-1 --resource-type EBS_SNAPSHOT \
  --retention-period RetentionPeriodValue=7,RetentionPeriodUnit=DAYS \
  --description "Undo window for deleted snapshots"

Archive the ones you must keep but will rarely read:

aws ec2 modify-snapshot-tier --region us-east-1 --snapshot-id snap-0e5d2a7f31 \
  --storage-tier archive

An archived snapshot is stored as a full copy of its data, not as increments. Archiving one that shares most of its blocks with newer snapshots can cost more, not less. Archive works best for the last snapshot of a volume you deleted, kept for the record. You pay for at least 90 days, and $0.03 per GB to bring it back:

aws ec2 restore-snapshot-tier --region us-east-1 --snapshot-id snap-0e5d2a7f31 \
  --temporary-restore-days 5

Google Cloud snapshots

Compute Engine snapshots work the same way: incremental, $0.05 per GB-month for standard snapshots in us-central1 as of October 2026 (disk pricing). List the ones older than 180 days in a project, with storageBytes, which is what each one actually stores:

gcloud compute snapshots list --project=my-project \
  --filter="creationTimestamp<-P180D" \
  --format="table(name,sourceDisk.basename(),diskSizeGb,storageBytes,creationTimestamp)"

Check the source disk and any snapshot schedule the same way as on AWS, then delete:

gcloud compute snapshots delete snap-orders-db-0412 --project=my-project --quiet

For long-term copies, Google Cloud offers archive snapshots at about $0.019 per GiB-month, plus about $0.019 per GiB to restore and a 90-day minimum. You choose the type when you create the snapshot, for example gcloud compute snapshots create orders-db-2026 --source-disk=orders-db --source-disk-zone=us-central1-a --snapshot-type=ARCHIVE.

Set retention so they expire on their own

Every snapshot schedule should come with a retention rule. On AWS, Data Lifecycle Manager can take and expire snapshots for every volume with a given tag. This policy snapshots volumes tagged backup=daily at 03:00 UTC and keeps the last 14:

aws dlm create-default-role --region us-east-1 --resource-type snapshot
cat > policy.json <<'JSON'
{
  "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
  "ResourceTypes": ["VOLUME"],
  "TargetTags": [{ "Key": "backup", "Value": "daily" }],
  "Schedules": [{
    "Name": "daily-keep-14",
    "CreateRule": { "Interval": 24, "IntervalUnit": "HOURS", "Times": ["03:00"] },
    "RetainRule": { "Count": 14 },
    "CopyTags": true
  }]
}
JSON
aws dlm create-lifecycle-policy --region us-east-1 \
  --execution-role-arn arn:aws:iam::123456789012:role/AWSDataLifecycleManagerDefaultRole \
  --description "Daily snapshots keep 14" --state ENABLED \
  --policy-details file://policy.json

If you use AWS Backup instead, give every backup rule a lifecycle that deletes recovery points after a set number of days.

On Google Cloud, a snapshot schedule carries its own retention:

gcloud compute resource-policies create snapshot-schedule daily-keep-14 \
  --project=my-project --region=us-central1 \
  --daily-schedule --start-time=03:00 --max-retention-days=14
gcloud compute disks add-resource-policies orders-db --project=my-project \
  --zone=us-central1-a --resource-policies=daily-keep-14

Manual snapshots taken before a change are the ones schedules never catch. Tag them with a delete-after date when you take them, and review the list above once a month. Omni FinOps does that review on every refresh, flagging snapshots older than 180 days that back no AMI, with the most each one can save.

If the snapshots you found came from volumes that no longer exist, check for the volumes’ siblings too: unattached EBS volumes are the other half of the same cleanup.

Mateo Vargas writes Omni FinOps’ cost guides, from the same rules the product runs on every refresh. Prices are list prices in us-east-1 or us-central1 as of October 11, 2026; check your region’s pricing page.

More guides