Snowflake Warehouse Right-Sizing: How to Stop Paying for Compute You Never Use
When we audit a new Snowflake account, the single fastest dollar we find is almost never a clever query rewrite. It's a warehouse that's been running at Large or X-Large since the day someone created it "to be safe" — doing work that a Small would finish in roughly the same wall-clock time, at a quarter of the credit burn.
This is the warehouse right-sizing problem, and it's Leak #2 in our breakdown of why your Snowflake bill is so high. It deserves its own deep-dive because the fix is unusually low-risk: you're not rewriting SQL, changing schemas, or touching a single downstream dashboard. You're changing one number on a warehouse and watching the bill drop.
This post shows you exactly how to find the warehouses that are mis-sized — in both directions — using nothing but SNOWFLAKE.ACCOUNT_USAGE, and how to resize them without anyone noticing except your finance team.
Why warehouse size is the most expensive decision you forget about
Snowflake credits scale with warehouse size on a brutally simple curve. Each step up doubles the credit-per-hour rate:
| Warehouse size | Credits per hour | Relative cost |
|---|---|---|
| X-Small | 1 | 1x |
| Small | 2 | 2x |
| Medium | 4 | 4x |
| Large | 8 | 8x |
| X-Large | 16 | 16x |
| 2X-Large | 32 | 32x |
| 3X-Large | 64 | 64x |
| 4X-Large | 128 | 128x |
The trap is that a bigger warehouse doesn't make most queries proportionally faster. Doubling the size doubles the compute (more nodes, more memory, more parallelism), so a query that's bottlenecked on a large scan or a heavy join may run roughly twice as fast. But a query that's small, that's bottlenecked on a single step, or that's mostly waiting — those run at almost the same speed on a Small as on an X-Large. You pay 8x the credits for a few seconds of improvement nobody asked for.
That asymmetry is the entire opportunity. Right-sizing isn't about finding the smallest possible warehouse — it's about finding the smallest warehouse that still completes the work without spilling to disk or missing a performance SLA. Get that right and a 30–50% compute reduction is a routine outcome, not an aggressive one.
The two ways a warehouse is mis-sized
There are exactly two failure modes, and you have to check for both before you touch anything.
Oversized is the common, expensive one: the warehouse is bigger than the workload needs. Queries finish fast, there's no memory pressure, and concurrency is low — you're paying for headroom you never use. This is where the savings live.
Undersized is the one that bites back: the warehouse is too small for its workload, so queries run out of memory and spill data to local or remote storage. Remote spilling can make a query 10x slower. If you blindly shrink a warehouse that's already spilling, you'll make performance worse and someone will (rightly) escalate.
The good news: ACCOUNT_USAGE tells you which is which, per warehouse, with no guesswork.
Step 1: Find oversized warehouses (where the money is)
Start by ranking warehouses by how much you spend on them versus how hard they actually work. This query pulls 30 days of credit consumption alongside the query profile for each warehouse — median runtime, how often queries spill, and how concurrent the workload really is.
WITH spend AS (
SELECT
warehouse_name,
SUM(credits_used) AS credits_30d
FROM snowflake.account_usage.warehouse_metering_history
WHERE start_time >= DATEADD('day', -30, CURRENT_DATE())
GROUP BY 1
),
profile AS (
SELECT
warehouse_name,
COUNT(*) AS query_count,
MEDIAN(execution_time) / 1000 AS median_exec_seconds,
MAX(warehouse_size) AS warehouse_size,
SUM(CASE WHEN bytes_spilled_to_local_storage > 0 THEN 1 ELSE 0 END) AS queries_spilled_local,
SUM(CASE WHEN bytes_spilled_to_remote_storage > 0 THEN 1 ELSE 0 END) AS queries_spilled_remote
FROM snowflake.account_usage.query_history
WHERE start_time >= DATEADD('day', -30, CURRENT_DATE())
AND warehouse_name IS NOT NULL
AND execution_time > 0
GROUP BY 1
)
SELECT
s.warehouse_name,
p.warehouse_size,
s.credits_30d,
p.query_count,
p.median_exec_seconds,
p.queries_spilled_local,
p.queries_spilled_remote
FROM spend s
JOIN profile p ON s.warehouse_name = p.warehouse_name
ORDER BY s.credits_30d DESC;Read the result like this. A warehouse that is Large or bigger, burning a meaningful share of your credits, with a low median execution time (queries finishing in a few seconds) and zero spilling, is an oversized candidate. It's doing small-warehouse work on big-warehouse hardware. Those are your first downsizing targets, ranked by credits_30d so you tackle the biggest dollars first.
The columns that matter: median_exec_seconds tells you whether the work is actually heavy, and the two queries_spilled_* counts tell you whether there's any memory pressure justifying the current size. If both spill counts are zero and queries are fast, the warehouse has nothing to lose by shrinking.
Step 2: Confirm the warehouse isn't quietly spilling
Before you downsize anything, run the spill check explicitly. Spilling is the single signal that says "this warehouse is already at or below the right size — do not shrink it." This query surfaces the warehouses and queries where memory pressure is real.
SELECT
warehouse_name,
warehouse_size,
COUNT(*) AS spilling_queries,
ROUND(AVG(bytes_spilled_to_local_storage) / POWER(1024, 3), 2) AS avg_local_spill_gb,
ROUND(AVG(bytes_spilled_to_remote_storage) / POWER(1024, 3), 2) AS avg_remote_spill_gb,
ROUND(SUM(bytes_spilled_to_remote_storage) / POWER(1024, 3), 2) AS total_remote_spill_gb
FROM snowflake.account_usage.query_history
WHERE start_time >= DATEADD('day', -30, CURRENT_DATE())
AND warehouse_name IS NOT NULL
AND (bytes_spilled_to_local_storage > 0 OR bytes_spilled_to_remote_storage > 0)
GROUP BY 1, 2
ORDER BY total_remote_spill_gb DESC;Any warehouse showing up here with meaningful total_remote_spill_gb is undersized for at least part of its workload. Don't shrink these — and if remote spill is heavy, consider sizing up one step, or better, splitting the heavy queries onto their own warehouse so you're not paying big-warehouse rates for the whole workload just to satisfy a few memory-hungry jobs.
A warehouse that appears in neither this list nor with high concurrency, but does appear as Large+ in Step 1, is the clean right-sizing win: oversized, no memory pressure, safe to shrink.
Step 3: Quantify the dollars before you change anything
Right-sizing is easy to sell to stakeholders when you can put a number on it. Because each size step is a clean 2x, downsizing a warehouse by one size cuts its compute roughly in half — assuming runtime stays flat, which for oversized warehouses it largely does. This query estimates the monthly credit savings of dropping each warehouse one size.
WITH wh_spend AS (
SELECT
warehouse_name,
SUM(credits_used) AS credits_30d
FROM snowflake.account_usage.warehouse_metering_history
WHERE start_time >= DATEADD('day', -30, CURRENT_DATE())
GROUP BY 1
)
SELECT
warehouse_name,
credits_30d,
ROUND(credits_30d / 2, 1) AS credits_if_one_size_smaller,
ROUND(credits_30d / 2, 1) AS estimated_monthly_credit_savings,
ROUND(credits_30d / 2 * 3.00, 2) AS estimated_monthly_dollar_savings_at_3usd
FROM wh_spend
ORDER BY credits_30d DESC;Swap the 3.00 for your actual effective credit price (Standard, Enterprise, and Business Critical editions differ, and your contract rate may differ again). The output gives you a ranked, dollarized list of right-sizing opportunities you can take straight into a planning conversation. Pair it with our Snowflake cost calculator if you want to model a few scenarios before committing.
How to actually resize a warehouse safely
The change itself is one statement:
ALTER WAREHOUSE analytics_wh SET WAREHOUSE_SIZE = 'SMALL';It takes effect immediately and is fully reversible — no rebuild, no downtime, no data movement. That's what makes right-sizing the lowest-risk lever in Snowflake cost optimization. But run it through a short, disciplined process rather than eyeballing it:
First, resize one step at a time. Go from X-Large to Large, not X-Large to Small. The whole point is to find the floor without falling through it, and one step halves the cost already.
Second, watch for new spilling after the change. Re-run the Step 2 spill query against the day or two following each resize. If remote spill appears where there was none, you've gone one step too far — step back up. Local spill in small amounts is usually acceptable; remote spill is the line you don't cross.
Third, separate workloads instead of sizing for the worst case. The most common reason a warehouse is oversized is that it serves a mix — light BI dashboards plus one nightly heavy transform — and it was sized for the transform. Move the heavy job to its own warehouse, size that one appropriately, and let the dashboards run on a Small. You stop paying X-Large rates around the clock for a job that runs twenty minutes a night.
Fourth, use multi-cluster for concurrency, not a bigger size. If queries are queuing (check queued_overload_time in QUERY_HISTORY, or avg_queued_load in WAREHOUSE_LOAD_HISTORY), the problem is too many concurrent queries, not queries that are individually too big. Sizing up burns credits without fixing the queue. A multi-cluster warehouse that scales out and back in handles the concurrency at far lower cost.
Where teams get right-sizing wrong
A few cautions, because right-sizing done carelessly can erode trust in the whole cost program.
Don't optimize for the smallest warehouse — optimize for the right one. The goal is the smallest size that meets the SLA, not the smallest size that runs. A query that takes 4 seconds instead of 3 on a downsized warehouse is a great trade; a critical pipeline that now spills to remote storage and runs 10x slower is not. Always check the spill query after a change.
Watch warehouses with bursty, infrequent heavy jobs. A warehouse that's idle most of the month but runs one enormous quarter-end job will look oversized in a 30-day median and undersized during that job. For these, separate the heavy job onto its own right-sized warehouse rather than shrinking the shared one.
Re-measure after you change query patterns. Right-sizing is not a one-time event. New pipelines, growing data volumes, and added concurrency all shift the right size over time. Re-run the Step 1 and Step 2 queries monthly — or have them alert you — so warehouses don't quietly drift back into oversized territory.
Account for the auto-suspend interaction. Right-sizing and idle-time control work together. A correctly-sized warehouse that still sits running idle between queries is only half-optimized — pair this work with aggressive AUTO_SUSPEND settings, which we cover in our pillar guide on Snowflake bill drivers.
Bottom line
Warehouse right-sizing is the highest-leverage, lowest-risk move in Snowflake cost optimization. The credit curve is a clean doubling, so every size you remove halves that warehouse's compute — and most warehouses are running at least one size too big "to be safe." Use the three queries above to rank oversized warehouses by dollars, confirm they aren't spilling, quantify the savings, then resize one step at a time while watching for new spill. Done methodically, a 30–50% compute reduction is a typical result, not an aggressive one — with zero changes to your queries, schemas, or dashboards.
If you'd rather have this done for you — every warehouse profiled, every safe resize identified and dollarized — that's exactly what we do in a free Snowflake cost assessment. We'll hand you a ranked right-sizing plan with the savings already calculated, and you decide what to act on.
---
This is part of our Snowflake cost-optimization series. Start with the pillar: Why Is Your Snowflake Bill So High? The 4 Biggest Leaks, or explore our Snowflake cost optimization services.
Follow Insignyx on LinkedIn
More on data, cloud & AI cost optimization.