Your CloudWatch Logs never expire - and that is a growing bill
Short version: By default, a CloudWatch log group keeps logs forever. Stored logs cost $0.03/GB-month, and every Lambda, ECS task, and API Gateway you have been running for years has been piling them up. Setting a retention policy is a one-line fix per log group. Here is how to find the never-expiring ones and cap them.
Why this quietly grows
Two things bill on CloudWatch Logs: ingestion ($0.50/GB, one-time) and
storage ($0.03/GB-month, forever). New log groups are created with
retentionInDays unset, meaning never expire, so storage grows without bound.
Nobody notices because it is a slowly rising line, not a spike. Debug logs from 2022
are still costing you money today.
Step 1 - Find log groups with no retention set
aws logs describe-log-groups \
--query 'logGroups[?retentionInDays==`null`].{Name:logGroupName,Bytes:storedBytes}' \
--output table
Sort by size to find the worst offenders first:
aws logs describe-log-groups \
--query 'reverse(sort_by(logGroups[?retentionInDays==`null`], &storedBytes))[:20].[logGroupName,storedBytes]' \
--output table
Step 2 - Set a retention policy
Pick a sane default (30/90/365 days depending on the log's purpose) and apply it:
aws logs put-retention-policy \
--log-group-name /aws/lambda/my-function \
--retention-in-days 90
Apply 90 days to every never-expiring group at once:
for lg in $(aws logs describe-log-groups \
--query 'logGroups[?retentionInDays==`null`].logGroupName' --output text); do
echo "setting 90d on $lg"
aws logs put-retention-policy --log-group-name "$lg" --retention-in-days 90
done
Retention applies going forward and prunes existing logs older than the window, so storage drops on its own after you set it.
The caveat: audit and compliance logs
Do not blanket-expire everything. CloudTrail, VPC Flow Logs, and anything under a compliance regime (PCI, HIPAA, SOC 2) may have a required minimum retention. For those, keep a longer window - or better, export to S3 (or S3 Glacier) for long-term retention at a fraction of CloudWatch's storage price, then expire the CloudWatch copy. Separate "I might debug this" logs (short retention) from "I must retain this" logs (export + policy).
Do it automatically
Cloud Cost Analyzer's cloudwatch-logs-retention rule flags log groups with infinite
(or excessive) retention and estimates the savings from capping them - alongside 88
other cost rules:
curl -sSL https://releases.dragonfractal.com/install.sh | sh
cca scan --provider aws
The agent runs in your environment with read-only access, so your AWS credentials never leave it. See the AWS setup and required IAM permissions
CLI Reference
aws logs describe-log-groupsaws logs put-retention-policy