Dmytro Oliinyk · 7 September 2026 · 6 min read
Guide · AWS
2 TB Out of S3 Glacier Deep Archive for $15 a Month
Storing 2 TB of backups cost me $20 a year. Getting them back out was supposed to cost as much as a new hard drive. It did not.
I have always been a backup activist, or a data hoarder as people would call it now. I have folders going back to 2005. I just do not like deleting anything, and it has saved me more than once. I know what 3-2-1 backup is and I have always pushed it on anyone who would listen. One copy local, one on a second medium, one in the cloud.
But one part of that 3-2-1 is the cloud. Once I started doing serious backups, I went past 2 TB fast. And just holding 2 TB as dead weight, paying something like $100 to $150 a year for data you would only need in a disaster, sounded stupid.
The trade I made#
So I read through half the internet and every piece of advice out there and settled on S3 Glacier Deep Archive. That got my backups down to about $20 a year, already a lot cheaper. The only problem was that getting the data back out would take at least two days of retrieval plus somewhere around $200 to $250 in egress cost. For 2 TB that is roughly the price of a new hard drive.
But I made my peace with it and figured it was worth it.
Getting the data back#
Recently I finally found drives for my NAS and wanted to get the data out of S3 for myself, because otherwise it just stays there forever. And that is where the problem shows up. Getting it out would cost as much as one HDD. So what do you do?
Luckily I found the answer, CloudFront's flat-rate plan, $15 for up to 50 TB with no extra bills. It was a lifesaver. Turned out to be really easy, almost no extra hassle.
CloudFront as the egress path#
Since November 2025 AWS has offered flat-rate plans for CloudFront. Free, Pro at $15 a month with 50 TB of traffic and 10 million requests, plus Business and Premium. Going over the allowance does not get billed, per the docs the worst case is throttled delivery if you stay massively over it for months. No annual contract.
The second part is older and less known. Data transfer from S3 to CloudFront costs nothing, unlike serving straight from S3 to the internet, which runs around $0.09 per GB. Put the two together, hang CloudFront in front of the bucket, pay $15 a month, and the entire egress up to 50 TB is already covered. 2 TB is not even close to that limit.
Restoring from Deep Archive#
CloudFront does not take one thing off your hands. Deep Archive is cold storage, the objects are simply not readable until you restore them, no matter which path you serve them through afterward. Restore is mandatory, Bulk tier up to 48 hours, Standard tier up to 12 hours, more expensive for the speed. I had 194 objects, a good 2 TB. Bulk retrieval costs $0.0025 per GB, which comes to $5.12 for my roughly 2 TB. Standard would have been eight times pricier at $0.02 per GB and $40.96, just to save twelve hours instead of forty eight. Add that to the $15, and it is still nothing next to what direct egress would have cost.
I kicked off the Bulk restore for all 194 objects with a list from list-objects-v2 and a loop over restore-object.
aws s3api list-objects-v2 \
--bucket your-bucket \
--query 'Contents[].Key' \
--output text | tr '\t' '\n' > keys.txt
while read -r key; do
aws s3api restore-object \
--bucket your-bucket \
--key "$key" \
--restore-request '{"Days":7,"GlacierJobParameters":{"Tier":"Bulk"}}'
done < keys.txt
Days sets how long the restored copy stays available before it freezes again, seven days gave me enough buffer.
OAC and WAF, already configured#
I expected some IAM and bucket-policy work by hand. I did not have to touch any of it. Origin Access Control and the AWS WAF rules were already there.
Cyberduck cannot do that#
I assumed I could browse the bucket through the CloudFront domain in Cyberduck the way I always do. Did not work, CloudFront has no listing, it only serves the exact path you ask for, no folder tree. It was not really a problem, I already had the key list from the restore step. A small script builds a URL from each line and pulls it with curl in parallel, folder structure included.
domain="dXXXXXXXXXXXXX.cloudfront.net"
cat keys.txt | xargs -P 8 -I{} bash -c '
key="{}"
mkdir -p "$(dirname "$key")"
encoded=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$key")
curl -fsSL -C - -o "$key" "https://'"$domain"'/$encoded"
'
-C - tells curl to resume an interrupted download exactly where it stopped, no small thing with 2 TB running overnight. And urllib.parse.quote keeps spaces and special characters in the path from breaking the URL.
Thirty hours of waiting#
Bulk tier is rated up to 48 hours, mine came in at around thirty. In between there was nothing to do except run a small script against the list now and then to see how many were done.
#!/usr/bin/env bash
total=$(wc -l < keys.txt)
finished=0
while read -r key; do
status=$(aws s3api head-object --bucket your-bucket --key "$key" --query Restore --output text)
[[ "$status" == *'ongoing-request="false"'* ]] && finished=$((finished + 1))
done < keys.txt
echo "Done: $finished / $total"
By the end it read Done: 194 / 194.
What it cost#
| Line item | Cost |
|---|---|
| CloudFront Pro, one month | $15 |
| Bulk retrieval, 2048 GB | $5.12 |
| Standard retrieval, for comparison | $40.96 |
| Direct S3 egress, for comparison | about $170 to $180 |
The AWS bill has not landed yet, but the order of magnitude holds. Around $20 for the whole retrieval, not the price of a hard drive. If you only need the distribution for this one retrieval, downgrade back to pay-as-you-go once you are done, the switch only takes effect at the next billing cycle, so do not wait too long.
One more point, unrelated to money. Without extra configuration CloudFront serves these objects to anyone who knows the exact URL. For a single short retrieval that was fine by me, I skipped the WAF IP allowlist since I only needed the distribution up for this one pull.