Redshift and Athena bill for opposite things. A provisioned Redshift cluster charges by the node-hour whether anyone runs a query or not. Athena charges nothing until a query runs, then bills for every byte it reads. Redshift Serverless sits in between: compute seconds while queries run, nothing while the warehouse is quiet. The waste on each looks different, so the fixes are different too.
This is the pass we do on an analytics bill. Where we mention prices, they’re us-east-1 list prices at the time of writing; the Redshift pricing page and the Athena pricing page have your region.
Where the money goes
Start with an inventory, because analytics teams accumulate clusters the way other teams accumulate EC2 instances. Everything below is read-only:
# Provisioned clusters: node type, count, status and age
aws redshift describe-clusters \
--query 'Clusters[].[ClusterIdentifier,NodeType,NumberOfNodes,ClusterStatus,ClusterCreateTime]' \
--output table
# Serverless workgroups: base and maximum RPUs
for w in $(aws redshift-serverless list-workgroups --query 'workgroups[].workgroupName' --output text); do
aws redshift-serverless get-workgroup --workgroup-name "$w" \
--query 'workgroup.[workgroupName,baseCapacity,maxCapacity,status]' --output text
done
# Athena workgroups and their per-query scan limit (None means no limit)
for g in $(aws athena list-work-groups --query 'WorkGroups[].Name' --output text); do
aws athena get-work-group --work-group "$g" \
--query 'WorkGroup.[Name,Configuration.BytesScannedCutoffPerQuery,Configuration.PublishCloudWatchMetricsEnabled]' \
--output text
done
Three things usually fall out: a provisioned cluster that has been available around the clock for years, a Serverless workgroup on its default base capacity, and an Athena primary workgroup with no scan limit.
Redshift Serverless: RPU-hours and base capacity
Serverless measures compute in Redshift Processing Units. Each RPU carries 16 GB of memory, and you pay per RPU-second while queries run, with a 60-second minimum each time the workgroup wakes. In us-east-1 the rate is $0.375 per RPU-hour. Storage is billed separately at the same managed-storage rate as RA3 clusters, and an idle workgroup costs nothing in compute.
The number that decides your bill is base capacity: the RPUs every query starts with. The default is 128, which at $0.375 is $48 for every hour of query activity before any scaling. You can set it lower in steps of 8, down to 8 RPUs (AWS has lowered this floor over time, so check the console for the current minimum), and maxCapacity caps how far the workgroup scales under load.
A two-minute query on 128 RPUs costs 128 × 120 seconds, about 4.3 RPU-hours, or $1.60. The same query on 32 RPUs might take five minutes and cost $1.00. Serverless rewards the smallest base capacity that meets your latency target.
The system view sys_serverless_usage records compute per minute, and AWS documents this query for turning it into a daily bill:
-- Read-only: RPU-hours charged per day, last 30 days
select trunc(start_time) as day,
sum(charged_seconds) / 3600.0 as rpu_hours,
sum(charged_seconds) / 3600.0 * 0.375 as usd_us_east_1
from sys_serverless_usage
where start_time > dateadd(day, -30, getdate())
group by 1
order by 1;
Then set a usage limit. Serverless supports limits on RPU-hours per day, week or month, with an action of log, alert through SNS, or turn off user queries. aws redshift-serverless list-usage-limits shows whether any exist. A workgroup without one is an open tab.
When provisioned wins. Serverless is cheaper for anything bursty and more expensive for anything that runs flat out. A 128-RPU workgroup busy 24 hours a day is about $35,000 a month. Two ra3.4xlarge nodes, at $3.26 an hour each, are about $4,760 a month on demand, and reserved nodes take that lower. If queries are running more than about a third of the time, do the arithmetic with your own charged_seconds before moving. The ComputeSeconds and ComputeCapacity metrics in CloudWatch tell the same story without a database login.
Pause and schedule provisioned clusters
A provisioned cluster bills per node-hour from the moment it’s available. Pausing stops compute charges; you keep paying for managed storage and backups, and reserved node charges continue because they were committed in advance. Resuming takes a few minutes and the cluster returns with its data, users and endpoint intact.
A cluster used weekdays from 7 a.m. to 7 p.m. is busy 60 of the week’s 168 hours. Pausing it the rest of the time removes 64% of its compute cost. Redshift’s scheduled actions pause and resume on a cron schedule with no Lambda involved:
# Read-only: existing pause/resume schedules
aws redshift describe-scheduled-actions \
--query 'ScheduledActions[].[ScheduledActionName,Schedule,TargetAction,State]' \
--output table
# Read-only: hourly CPU over the last week, to see when a cluster is actually used
aws cloudwatch get-metric-statistics --namespace AWS/Redshift \
--metric-name CPUUtilization \
--dimensions Name=ClusterIdentifier,Value=analytics-dev \
--start-time "$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-7d +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 3600 --statistics Average \
--query 'Datapoints | sort_by(@, &Timestamp)[].[Timestamp,Average]' --output text
Dev and test clusters are where this pays off most. The pattern we keep seeing is a two-node ra3.4xlarge copy of production that a few engineers query for an hour or two a day, costing about $4,760 a month in compute around the clock. The same work on a Serverless workgroup at 8 RPUs costs $3 per hour of query time: two hours a day on 22 working days is about $132. If the dev warehouse must stay provisioned, pause it outside working hours. Either way, read DatabaseConnections for the last month first; a cluster with zero connections isn’t a dev warehouse, it’s an orphaned resource.
Reserved nodes
Reserved nodes are Redshift’s version of EC2 Reserved Instances: a one- or three-year commitment to a specific node type in a specific region, paid no, partial or all upfront, and AWS puts the saving at up to 75% against on-demand for the longest term paid in full. They apply only to provisioned clusters. Serverless gets no discount from them, and there is no Savings Plan for Redshift.
Rules we apply before buying:
- Reserve only what has run continuously for three months and will run for the term. A reservation on a cluster you later pause keeps billing.
- Match the node type exactly. A reservation for ra3.4xlarge does nothing for ra3.xlplus.
- Resize first, reserve second. If the cluster is about to move to RA3 or Serverless, wait.
- Prefer one-year terms unless the workload has been stable for years.
aws redshift describe-reserved-nodes lists what you hold. Compare its node counts against describe-clusters above; reservations that outlive their clusters are common after a migration, and the only fix is to use them until they expire. Our Reserved Instances vs Savings Plans guide covers the baseline arithmetic, which is the same.
RA3 managed storage and snapshots
DC2 nodes bundle local SSD with compute, so when data outgrows the disks you add nodes you don’t need for compute. RA3 nodes split the two: compute is the node price, and data lives in Redshift Managed Storage, billed per GB-month on the compressed size at $0.024 in us-east-1, the same rate Serverless uses. Growing data on RA3 costs storage money only, and you size compute for the queries.
Storage has two leaks. Tables nobody has queried in a year cost the same as hot ones; unload them to S3 and read them through Spectrum, or drop them. And manual snapshots never expire. Automated snapshots within the retention period cost nothing extra on RA3; manual snapshots and anything kept past retention bill at the managed-storage rate. aws redshift describe-cluster-snapshots --snapshot-type manual sorted by SnapshotCreateTime usually turns up snapshots of clusters deleted long ago. Agree with the data owners before deleting any; the annual audit snapshot exists for a reason.
Concurrency scaling and Spectrum
Two features on provisioned clusters bill by the second on top of the node price.
Concurrency scaling adds transient clusters when queries queue. Each provisioned cluster earns one hour of free concurrency-scaling credit for every 24 hours it runs, accruing up to 30 hours. Past the credit, every second a scaling cluster runs is billed at the main cluster’s on-demand rate. A cluster that leans on it for hours every day is undersized, or has a few queries that need their own workload-management queue.
Redshift Spectrum reads data in S3 and bills $5 per terabyte scanned, with a 10 MB minimum per query. Everything below about Parquet, partitions and compression applies to Spectrum byte for byte, because it’s the same mechanism.
On Serverless both are included in the RPU-hours. On provisioned clusters, set usage limits so a runaway query shows up as an alert rather than an invoice. aws redshift describe-usage-limits lists limits for both features, and an empty list is the usual finding.
Athena bills per terabyte scanned
Athena’s SQL engine charges $5 per terabyte scanned in us-east-1, rounded up to the nearest megabyte with a 10 MB minimum per query. DDL, failed queries and partition-management statements are free. A cancelled query bills for what it read before you cancelled it, and a CREATE TABLE AS SELECT bills for the data its SELECT scans.
That one price makes the goal clear: read fewer bytes. A 2 TB scan costs $10 whether it takes ten seconds or ten minutes. Find the expensive ones with per-query statistics:
# Read-only: data scanned by the last 50 queries in a workgroup, largest first
IDS=$(aws athena list-query-executions --work-group primary --max-results 50 \
--query 'QueryExecutionIds' --output text)
aws athena batch-get-query-execution --query-execution-ids $IDS \
--query 'QueryExecutions[].[Statistics.DataScannedInBytes,Status.State,Query]' \
--output text | sort -rn | head -20
For the trend, turn on Publish query metrics to CloudWatch on each workgroup and chart ProcessedBytes in the AWS/Athena namespace by WorkGroup. The same metric drives the data usage alerts in the next section.
Athena also sells provisioned capacity: a fixed number of data processing units, 24 at minimum, billed per DPU-hour instead of per terabyte. It suits a heavy, steady load with a large, predictable per-TB bill. For anything bursty, keep paying per scan.
Two costs sit beside the Athena line. Every query writes a result file and a metadata file to the workgroup’s S3 output location, and they pile up forever without a lifecycle rule; our S3 lifecycle guide has the pattern. And the Glue Data Catalog charges above its free tier of a million stored objects and a million requests a month, with every partition counting as an object.
Partitions, Parquet and compression
Three changes to how data sits in S3 cut scanned bytes more than any query rewrite.
Partition on the columns you filter by. With an s3://bucket/events/year=2026/month=10/day=29/ layout and a partitioned table, WHERE year = 2026 AND month = 10 reads one month’s folders and nothing else. Unpartitioned, the same query reads every file in the table. If partitions follow a pattern, partition projection computes them from table properties at query time, so Athena skips the Glue GetPartitions calls and you never run another ALTER TABLE ADD PARTITION.
Store columnar formats. CSV and JSON must be read in full, every column of every row. Parquet and ORC store columns separately with min/max statistics per block, so a query touching 3 of 30 columns reads roughly a tenth of the file, and blocks whose statistics rule them out are skipped. Converting is a CREATE TABLE AS SELECT or an UNLOAD into Parquet: one conversion scan, then every later query reads the smaller files.
Compress. Athena bills on compressed bytes read from S3, so Snappy or Zstd inside Parquet is a direct discount on every query. For raw text you can’t convert yet, prefer a splittable codec such as bzip2 over gzip, so one large file doesn’t become one slow reader.
File size matters too. Aim for at least 128 MB per file; thousands of small files mean thousands of S3 GET requests, at $0.0004 per 1,000, and more time planning than reading. If the writer can’t produce large files, Apache Iceberg tables let Athena compact them later with OPTIMIZE.
Finally, write SELECT lists, not SELECT *; LIMIT does not reduce scanned bytes once a query has an ORDER BY, GROUP BY or join. If the source is CloudWatch logs, exporting them to S3 in a partitioned, compressed layout is cheaper on both sides.
Workgroup limits, result reuse and result caching
Athena workgroups carry three controls that cost nothing to turn on.
| Control | Scope | What it does |
|---|---|---|
| Per-query data usage control | Workgroup | Cancels any query that scans more than the limit (10 MB minimum) |
| Workgroup data usage alerts | Workgroup, hourly or daily | Publishes to SNS when total ProcessedBytes crosses a threshold |
| Query result reuse | Per query, up to 7 days | Returns the previous result for an identical query, scanning nothing |
The per-query cutoff is the one we set first. A 1 TB limit stops the accidental SELECT * FROM events over five years of data at $5, not $50. Put analysts, scheduled reports and ad hoc exploration in separate workgroups, each with its own cutoff and output bucket, and enforce workgroup settings so a client can’t override them. The data usage control page covers both limit types.
Query result reuse is for dashboards and scheduled reports that run the same SQL every few minutes. You set a maximum age of up to 7 days; a repeat of the same query in the same workgroup within that window is served from the stored result and bills nothing. It’s a per-query setting, so it has to be on in the BI tool’s connection or the client code, not just the console.
Redshift has its own version. Result caching is on by default and returns the stored result for a repeated query when the underlying tables haven’t changed. On provisioned clusters that frees compute; on Serverless, a cached hit doesn’t keep RPUs busy. Leave enable_result_cache_for_session at its default unless you’re benchmarking.
FAQ
Is Redshift Serverless cheaper than a provisioned cluster?
For bursty or intermittent use, usually yes, because idle time costs nothing. For a warehouse busy most of the day, provisioned nodes with reservations are cheaper. Sum charged_seconds for a month and compare it with the node-hours you’d otherwise pay.
Does pausing a Redshift cluster stop all charges?
It stops compute charges. Managed storage, snapshot storage and any reserved node commitment keep billing while the cluster is paused.
Do Redshift reserved nodes apply to Serverless?
No. Reserved nodes cover provisioned clusters of the matching node type only. There is no commitment discount for Serverless RPU-hours.
How does Athena charge for a cancelled or failed query?
A failed query costs nothing. A cancelled query bills for the data it scanned before cancellation. DDL statements are free.
Start with the emptiest hours
Run the inventory at the top, then pull one week of CPUUtilization for every provisioned cluster and one month of ProcessedBytes for every Athena workgroup. The provisioned cluster with the most empty hours gets a pause schedule or a move to Serverless this week; the Athena workgroup with the biggest scan gets a per-query cutoff and its largest table converted to partitioned Parquet. Do one of each, wait a billing cycle, and read the two lines on the invoice before touching reservations.
Kostrix’s idle check does the first half on a schedule: it scans accounts read-only, lists Redshift clusters with no connections or near-zero CPU over the scan window, and prices each at the node rate it’s paying on the bill.
