awscloudwatchlogsobservabilitycost-optimization

CloudWatch Logs cost optimization: cutting the $0.50/GB bill

Ingestion at $0.50/GB dominates CloudWatch Logs bills. Source-side filtering, structured logging, IA tier, and S3 migration for high-volume groups typically cut the bill 50-80%. Here's the full breakdown and migration playbook.

The C3X Team··11 min read

Quick answer

CloudWatch Logs ingestion at $0.50/GB is the single biggest cost driver. Cut it with source-side filtering (drop DEBUG/INFO, drop noisy paths), structured logging (filter cleanly), shorter retention (30 days default), and migration to S3+Athena for long-term/high-volume logs. Most teams cut CW Logs bills 50-80% with these four moves.

CloudWatch Logs is the easiest observability solution on AWS: write to stdout, IAM-grant the role, and logs appear. That simplicity hides a cost trap. Ingestion is $0.50 per GB. A moderately-sized service producing 100 GB of logs per day pays $1,500/month just to ingest, before storage, before queries.

This post breaks down the full cost model and walks through the optimizations that reduce CW Logs spend without losing observability.

The complete CloudWatch Logs pricing

us-east-1, June 2026:

Standard log class

  • Ingestion: $0.50 per GB
  • Storage: $0.03 per GB-month
  • Logs Insights queries: $0.005 per GB scanned
  • Live Tail: $0.01 per minute

Infrequent Access log class

  • Ingestion: $0.25 per GB (half of Standard)
  • Storage: $0.05 per GB-month (more than Standard)
  • Logs Insights queries: $0.005 per GB scanned (same)
  • Higher query latency

Other charges

  • Vended Logs (VPC Flow Logs to S3): $0.25 per GB
  • Log Group metric filters: free (one of the few free CW features)
  • Data Protection (PII scrubbing): $0.12 per GB processed
  • Cross-account/cross-region log delivery: standard data transfer

Ingestion dominates. For most workloads, 80-90% of the CW Logs bill is ingestion, ~10% storage, the rest queries. Optimizations focused on reducing ingestion volume have the biggest impact.

Where ingestion volume comes from

Identifying the noisy sources is the first step. CloudWatch provides incoming-bytes metrics per log group. Sort log groups by IncomingBytes for the last 7 days — the top 10 usually account for 80%+ of total ingestion.

Common offenders:

  • Verbose application logging (every request logged at INFO)
  • Health check logs (every 5 seconds × 100 instances)
  • Library noise (database driver logs, HTTP client retries)
  • Stack traces logged multiple times (handler + middleware + global)
  • VPC Flow Logs enabled at high sampling rate
  • RDS Performance Insights / enhanced monitoring
  • Lambda function logs from high-frequency invocations

Optimization 1: Source-side filtering

The cheapest log is the one not shipped. Most applications log DEBUG/INFO by default in production and only need WARN+ for operational purposes. Switching the default level cuts volume 70-90%.

For services already structured-logging, drop noisy events at the agent. CloudWatch Agent supports filter blocks:

{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [{
          "file_path": "/var/log/app.log",
          "filter": {
            "type": "exclude",
            "expression": "GET /health"
          }
        }]
      }
    }
  }
}

For Lambda, drop noisy log lines in code before they go to stdout. Lambda CloudWatch ingestion is automatic, so anything you println() bills you.

Optimization 2: Aggressive retention

Default retention is "never expire." That's wrong for almost everything. Set retention to:

  • Production app logs: 30 days
  • Dev/staging app logs: 7 days
  • Audit logs (compliance): export to S3 with lifecycle rules
  • VPC Flow Logs: 7 days (or to S3 directly)
  • Lambda function logs: 14 days

For terraform-managed log groups:

resource "aws_cloudwatch_log_group" "app" {
  name              = "/aws/app/api"
  retention_in_days = 30
}

For existing log groups without retention set, AWS Config or a one-time script can apply retention to all log groups in bulk. Scan for groups with retention=null, set to a default.

Optimization 3: Structured logging + filter patterns

Switch from unstructured strings to JSON logs. Structured logs are smaller (no repeated label strings), and they enable cheap filter-based metric extraction without paying for full ingestion.

Example: instead of logging "Order 123 processed in 234ms", log:

{"event":"order_processed","order_id":123,"duration_ms":234}

Then create a metric filter on the log group that extracts duration_ms as a metric. The metric is free; you don't pay per-query to derive it.

Optimization 4: Move high-volume logs to S3

For log groups exceeding 50 GB/day, migrate ingestion to S3:

  • VPC Flow Logs: change destination from CloudWatch to S3 directly
  • ALB/NLB access logs: ship to S3, query via Athena
  • CloudFront logs: standard logs to S3, real-time logs to Kinesis Firehose
  • Application logs (high-volume tier): ship via Fluentbit to S3 instead of CloudWatch agent

Cost comparison for 1 TB/day of logs:

  • CloudWatch Logs Standard ingestion: $500/day = $15,000/month
  • CloudWatch Logs IA ingestion: $250/day = $7,500/month
  • S3 Standard storage (1 TB/day, 30-day retention): $30 ingest + $690 storage = $720/month
  • Add Athena queries (~50 queries/day, 100 GB scanned each): ~$25/day = $750/month
  • S3 total: $1,470/month vs $15,000 on CW Logs (10x cheaper)

Trade-off: Athena is less ergonomic than Logs Insights. For ops users querying frequently, CloudWatch wins. For data retention and bulk analysis, S3 + Athena wins.

Optimization 5: Logs Insights query discipline

Logs Insights bills $0.005 per GB scanned. A query over 500 GB costs $2.50. Engineers iterating on a noisy query can rack up bills fast.

Discipline patterns:

  • Always specify a time range (default is "last 1 hour" — verify)
  • Use filter expressions early in the pipe to reduce scanned bytes
  • Use fields to project only needed columns (smaller scan)
  • For repeated dashboards, use saved metrics extracted via filter patterns instead of re-querying

Optimization 6: Use Infrequent Access for archival-but-queryable

For log groups you ingest a lot of but rarely query (audit logs, security events), switch the log class to IA:

resource "aws_cloudwatch_log_group" "audit" {
  name              = "/aws/audit/events"
  retention_in_days = 90
  log_group_class   = "INFREQUENT_ACCESS"
}

IA halves ingestion cost ($0.25/GB vs $0.50/GB). Trade-off: higher storage cost ($0.05 vs $0.03/GB-month), no real-time subscriptions, slightly higher query latency. Right for write-heavy, read-rare workloads.

Migration playbook

Order of operations for a CloudWatch Logs cost reduction:

  1. Audit: list all log groups, sort by IncomingBytes (last 30 days), identify top 10. Most fleets find 3-5 groups accounting for 80% of cost.
  2. Quick wins: set retention on every group (default 30 days), delete groups with no recent activity.
  3. Source-side filtering: drop INFO/DEBUG, drop health checks, drop static asset logs.
  4. Structured logging: convert top apps to JSON logs with consistent fields.
  5. IA migration: switch audit/security log groups to Infrequent Access.
  6. S3 migration: for the largest 1-2 log groups (typically VPC Flow Logs or ALB access logs), migrate to S3 + Athena.
  7. Monitor: set up an Anomaly Detection alarm on AWS/Logs/IncomingBytes per log group.

Realistic outcome

For a typical mid-sized AWS account spending $10K/month on CloudWatch Logs:

  • Retention cleanup: -$1,000 (cleared storage backlog)
  • Source filtering: -$3,000 (50% ingestion reduction)
  • S3 migration of VPC Flow Logs: -$2,500
  • IA migration of audit logs: -$500
  • Total: -$7,000/month (70% reduction)

These numbers aren't theoretical — they're typical of the first-pass optimization on accounts that haven't tuned Logs before. The remaining $3K/month is operational logs that are genuinely useful, kept on CloudWatch because the team queries them daily.

Estimating with c3x

CloudWatch Logs volume is usage-based. Specify expected ingestion in c3x-usage.yml:

# c3x-usage.yml
resource_usage:
  aws_cloudwatch_log_group.app:
    monthly_data_ingested_gb: 500
    monthly_data_archived_gb: 2000
    monthly_data_scanned_gb: 100  # Logs Insights queries

c3x produces a cost estimate from these inputs, factoring the log class (Standard vs IA) configured on the resource.

FAQ

Why is CloudWatch Logs ingestion priced at $0.50 per GB?

CloudWatch Logs charges $0.50 per GB ingested (us-east-1, June 2026). At scale this dominates the bill: a service producing 1 TB/day of logs pays $15,000/month just to ingest, before any storage or query cost. The price reflects what the service stores by default — durable, queryable, multi-AZ. Workloads with high log volume often migrate to S3 + Athena or self-hosted Loki at 10-50x lower cost.

What's the cheapest CloudWatch Logs storage class?

Infrequent Access logs (introduced 2023) cost $0.05/GB-month vs $0.03/GB-month for Standard. Counterintuitively, IA is more expensive for storage. The IA value is in lower ingestion: $0.25/GB ingested (half of Standard). For workloads where you ingest a lot but query rarely, IA cuts ingestion cost in half. For workloads where you query often, Standard is cheaper because IA has higher query costs.

How much does Logs Insights query cost?

Logs Insights queries cost $0.005 per GB scanned. A query over 100 GB of logs costs $0.50. The pricing motivates scoped queries (narrow time range, specific log groups) and aggressive use of indexes/filter expressions to reduce data scanned. Compare to Athena over S3 at $5/TB scanned — Athena is more expensive per-scan but doesn't have ingestion fees, which usually dominate.

Should I disable log groups I don't use?

Yes, immediately. Log groups with retention set to 'never expire' silently accumulate. A Lambda function that's been running for 3 years with default config has 3 years of unbounded logs charging storage. Set retention to 30 days or less for non-critical logs. For archival, export to S3 and let lifecycle policies handle long-term retention at $0.023/GB-month.

Does log filtering at the source reduce CloudWatch bills?

Significantly. Filtering DEBUG/INFO logs at the application (only ship WARN+) cuts ingestion 70-90% for most apps. Other filters: drop health-check requests, drop static asset 200s, drop noisy library logs at the agent layer (CloudWatch Agent has filter config). Combined with structured logging, source-side filtering is the highest-leverage CW Logs optimization.

When should I migrate off CloudWatch Logs entirely?

When monthly ingestion exceeds ~2 TB. At that point you're paying $1,000+/month just to ingest. Alternatives: S3 + Athena (10x cheaper, less queryable), self-hosted Loki + Grafana (5-20x cheaper, more queryable, ops burden), DataDog/New Relic SaaS (similar price but more features). For under 500 GB/month, the CloudWatch operational simplicity is usually worth the cost.

Summary

CloudWatch Logs is convenient but expensive at scale. Ingestion at $0.50/GB dominates. Cut it with source-side filtering, aggressive retention, structured logging, and S3 migration for the highest-volume log streams. Typical first-pass optimization cuts CW Logs spend 50-80% without losing operational observability.

For data transfer cost interaction (cross-region log replication), see AWS data transfer costs explained. For the broader observability cost picture and self-hosted alternatives, see Self-hosting the cloud pricing API.

Try C3X on your own Terraform

Free and open source. No API key required. One command to install, one command to estimate.