# S3 Storage Tier Migration

> Flags cold, Standard-dominant S3 buckets with no lifecycle rule and prices a Standard-IA transition from real bytes.

Source: https://zop.dev/integrations/aws/recommendations/s3-storage-tier-migration

---

## Standard-IA halves storage for data you rarely read

S3 Standard-IA is designed for long-lived data accessed about once a month, per the
[storage class comparison](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html).
AWS's US East (N. Virginia) price list puts it at $0.0125 per GB-month against $0.023 for Standard.
The conditions matter as much as the price: Standard-IA has a 30-day minimum storage duration, bills
objects under 128 KB as 128 KB, and charges a per-GB retrieval fee. A bucket that is written once
and read rarely gains; one that is read constantly loses.

## Measuring size and access for a bucket

```bash
aws cloudwatch get-metric-statistics --namespace AWS/S3 --metric-name BucketSizeBytes \
  --dimensions Name=BucketName,Value=my-bucket Name=StorageType,Value=StandardStorage \
  --start-time 2026-09-22T00:00:00Z --end-time 2026-09-25T00:00:00Z \
  --period 86400 --statistics Average

aws cloudwatch get-metric-statistics --namespace AWS/S3 --metric-name AllRequests \
  --dimensions Name=BucketName,Value=my-bucket Name=FilterId,Value=EntireBucket \
  --start-time 2026-08-26T00:00:00Z --end-time 2026-09-25T00:00:00Z \
  --period 86400 --statistics Sum
```

`AllRequests` only exists after you add a request metrics configuration to the bucket.

## When a bucket qualifies

1. S3 Standard holds more of the bucket's bytes than any other class.
2. The bucket has no lifecycle policy managing transitions already.
3. It has a known monthly cost.
4. Request metrics exist and the average is below 1,000 requests a day. At or above that level,
   Standard-IA request and retrieval charges would eat the storage saving.
5. The per-bucket Standard to Standard-IA saving that ZopNight computes from the bucket's storage
   classes is positive.

## Buckets that are skipped

No request metrics means no finding, because ZopNight will not call a bucket cold without measuring
it. Buckets dominated by another class, buckets with a lifecycle policy, and buckets with no positive
saving are also skipped. A bucket that suits automatic tiering better is covered by
<a href="https://zop.dev/integrations/aws/recommendations/s3-intelligent-tiering-not-enabled">S3 Intelligent-Tiering Not Enabled</a>,
and one that has no lifecycle rule but no measured access data is not priced today;
<a href="https://zop.dev/integrations/aws/recommendations/s3-bucket-lifecycle-policy-missing">S3 Bucket Lifecycle Policy Missing</a>
is meant to cover it once ZopNight measures per-bucket cold data.

## Standard bytes times the rate gap

```text
saving = Standard GB x (Standard rate - Standard-IA rate)
capped at the bucket's monthly cost
```

At us-east-1 list prices, 10 TB in Standard saves about $107 a month once it sits in Standard-IA.

## Moving cold data to Standard-IA

1. Use Storage Lens or Storage Class Analysis to confirm which prefixes are cold.
2. Add a transition after 30 days, the earliest point lifecycle allows for Standard-IA:

```json
{"Rules":[{"ID":"standard-to-ia","Status":"Enabled","Filter":{"Prefix":"reports/"},
  "Transitions":[{"Days":30,"StorageClass":"STANDARD_IA"}]}]}
```

3. Apply it with `aws s3api put-bucket-lifecycle-configuration`, then watch retrieval charges on the
   next bill.
