A finding · Free · No signup

Your Snowflake warehouse may never be sleeping

Auto-suspend only saves money in gaps longer than the suspend window. A warehouse set to sleep after 10 minutes and queried every 4 never sleeps at all — it is billed as if it ran continuously, and the settings page shows nothing wrong.

Why the timer never reaches zero

AUTO_SUSPEND is a period of inactivity, measured in seconds. Every query that arrives restarts the count. So the setting does not ask “how long should this run?” — it asks “how big a gap counts as idle?”. If your gaps are consistently smaller than the window, the timer never reaches zero and the warehouse never suspends.

Snowflake bills compute per second, with a 60-second minimum each time the warehouse starts. Those two facts together are the whole trap. Per-second billing is what makes suspending worth doing; the restarting timer is what stops it happening. A dashboard on a five-minute refresh, a monitoring query on a cron, a reverse-ETL job every two minutes — each is cheap on its own and each one resets the clock.

Snowflake's own guidance says so: “the value you set should match the gaps, if any, in your query workload”. The setting gets chosen once, from a default or a blog post, and then the workload changes underneath it. Measuring the gaps is the step that gets skipped.

Three things to hold while reading the rest. This assumes a standard, single-cluster warehouse, and queries spread through the hour rather than fired in bursts and short relative to the gaps between them — true for the dashboard-shaped workloads this catches. It has to be a query that runs on the warehouse: a metadata-only query or a result-cache hit needs no running warehouse, so it should not hold the timer open either — though query history still lists them, which is one reason the metering query below is the one to trust. And suspension is not precise — Snowflake's suspend check “runs approximately every 30 seconds”, so a warehouse can idle a little past its setting.

What it costs, with numbers you can redo

One Large warehouse — 8 credits an hour, at Snowflake's published Gen1 rates — with a 10 min auto-suspend, taking 15 queries an hour across 10 hours a day, 22 days a month, at $3 a credit. Gen2, Snowpark-optimized and interactive warehouses bill at their own rates; check yours.

  1. The gap. 3,600 seconds ÷ 15 queries = 240 seconds between queries, if they are spread through the hour. That is 4 minutes, against a 10-minute window.
  2. So it never suspends. At that spacing no gap ever reaches the window, so the warehouse is awake for all 10 hours a day whether or not anything is running.
  3. The hours. 10 × 22 = 220 billed hours a month.
  4. The credits. 220 × 8 = 1,760 credits.
  5. The money. 1,760 × $3 = $5,280 a month, $63,360 a year — for a warehouse that spends essentially all of that time waiting.

What each setting would cost, same workload

Auto-suspendPaid for idling, monthlySaved vs 10 min
1 min$1,320$3,960
2 min$2,640$2,640
5 min$5,280—
10 min · the setting above$5,280—
15 min$5,280—
60 min$5,280—

Nothing below a minute appears in that table. Each resume bills a 60-second minimum, so a shorter window saves at most the length of the query itself — seconds — and the suspend check only runs about every 30 seconds anyway. The 1-minute row is the floor.

Check your own account in ten minutes

Two read-only queries. The first measures the gap your warehouses actually see and puts it next to their settings; the others answer the same question from the other end — how often each one really suspended, and what the idling cost. You need a role that can read the SNOWFLAKE database. On most accounts that is ACCOUNTADMIN; to hand it to someone else, grant imported privileges on database snowflake to role <theirs>.

-- Median gap between queries per warehouse, last 14 days,
-- next to what that warehouse is configured to do.
-- Read-only. Run the SHOW first: the second query reads its result set.
show warehouses;

with settings as (
  select "name"          as warehouse_name,
         "size"          as warehouse_size,
         "auto_suspend"  as auto_suspend_s
  from table(result_scan(last_query_id(-1)))
),
gaps as (
  select warehouse_name,
         -- end-to-start: the gap the warehouse actually sits idle for.
         -- start-to-start would fold the previous query's runtime into it.
         datediff(
           'second',
           lag(end_time) over (partition by warehouse_name order by start_time),
           start_time
         ) as gap_s
  from snowflake.account_usage.query_history
  where start_time >= dateadd(day, -14, current_timestamp())
    and warehouse_name is not null
    and upper(execution_status) = 'SUCCESS'
    -- Statements that actually run on the warehouse. A metadata-only query or
    -- a result-cache hit does not wake one, so it should not count as traffic.
    and query_type in ('SELECT','INSERT','UPDATE','DELETE','MERGE','COPY')
),
measured as (
  select warehouse_name,
         count(*)                                as gaps_measured,
         median(gap_s)                           as median_gap_s
  from gaps
  where gap_s is not null
  group by 1
)
select s.warehouse_name,
       s.warehouse_size,
       s.auto_suspend_s,
       m.median_gap_s,
       m.gaps_measured,
       case
         when s.auto_suspend_s is null or s.auto_suspend_s = 0 then 'never suspends (setting)'
         when m.median_gap_s is null                           then 'too few queries to judge'
         when m.median_gap_s < s.auto_suspend_s                then 'effectively always on'
         else 'suspends normally'
       end as verdict
from settings s
left join measured m on m.warehouse_name = s.warehouse_name
order by s.auto_suspend_s desc nulls first;

How to read it. A median gap shorter than auto_suspend_s usually means that warehouse never suspends — you are paying for its whole active day. An auto_suspend_s of 0 or null means auto-suspend is off entirely, which is the same bill arrived at honestly.

Where it lies: a bursty workload. A dashboard that fires ten tiles at once every half hour has a median gap near zero and suspends perfectly well between bursts. That is what the next two queries are for — and if they disagree with this one, believe them. They count what happened; this one infers it.

-- And the direct check: did it ever actually go to sleep?
select warehouse_name, count(*) as autosuspends
from snowflake.account_usage.warehouse_events_history
where timestamp >= dateadd(day, -14, current_timestamp())
  and event_name   = 'SUSPEND_WAREHOUSE'
  and event_reason = 'WAREHOUSE_AUTOSUSPEND'
group by 1
order by autosuspends;

A warehouse that burned credits for a fortnight and appears with a zero — or does not appear at all — never went to sleep once. That is not an inference; it is the absence of the event.

And then the number itself. Snowflake publishes a query for this on its own metering history page: subtract the credits it can attribute to running queries from the credits the warehouse burned, and what is left is what you paid for it to sit there. Not my arithmetic — theirs, against your account.

-- What the idling actually cost. Snowflake's own query.
select warehouse_name,
       sum(credits_used_compute)                as compute_credits,
       sum(credits_attributed_compute_queries)  as query_credits,
       sum(credits_used_compute)
         - sum(credits_attributed_compute_queries) as idle_credits
from snowflake.account_usage.warehouse_metering_history
where start_time >= dateadd(day, -30, current_date())
  and end_time   <  current_date()
group by 1
order by idle_credits desc;

Multiply idle_credits by your cost per credit and you have the real version of the estimate further up this page. If the two disagree, believe this one.

  • QUERY_HISTORY keeps 365 days and lags by up to 45 minutes, so do not run this and expect the last half hour.
  • WAREHOUSE_EVENTS_HISTORY also keeps 365 days but lags by up to three hours.
  • SHOW WAREHOUSES reports auto_suspend in seconds, and its output can be queried with RESULT_SCAN — which is the only reason these two things can be joined in one statement.
  • Cost per credit varies by edition, region and contract. Check yours before turning credits into money.

The trade-off is real

Suspending is not free. A running warehouse holds a cache of data from the queries it has already run, and “this cache is dropped when the warehouse is suspended, which might result in slower initial performance for some queries after the warehouse is resumed”. Snowflake asks you to weigh exactly that: “consider the trade-off between saving credits by suspending a warehouse versus maintaining the cache of data from previous queries to help with performance”.

So the right setting depends on the workload, and I am not going to tell you it is 60 seconds. Snowflake's own recommendation, if you enable auto-suspend at all, is “a low value (for example, 5 or 10 minutes or less)” — but a low value on a warehouse queried every four minutes is the case above, which saves nothing.

One more thing the docs are clear about: on a multi-cluster warehouse, auto-suspend applies to the whole warehouse, not to each cluster — the extra clusters are shut down by the scaling policy instead. Billing is the size rate multiplied by the number of running clusters, so “never suspends” there means at least one cluster's worth, continuously.

Whether to check is not a trade-off. A warehouse that has not suspended once in a fortnight is a fact about your bill, and nothing in the interface tells you.

Or let something else run it

warehouse-teardown is where the query above comes from. It is one file, open source, and read-only: it reads credits per warehouse, query start times, query attribution, table sizes and access history, and prints the warehouses that never suspend along with what the idle time cost. This runs the whole thing on obviously fake data — no credentials, nothing installed permanently — so you can see the output before pointing it at anything real:

pipx run --spec git+https://github.com/hikmahtech/warehouse-teardown warehouse-teardown --demo

It answers five of the 18 questions in the Data Platform Teardown. The other 13 need a person — no tool is going to tell you whether a new engineer can run your pipeline locally on day one.

Put your own numbers in

It opens on the example above. Change the size, the setting and the query rate to match one of your warehouses — and then check the rate against the SQL rather than trusting the estimate, because a guessed query rate is the input most likely to make this number wrong, in either direction.

Never actually suspends

Paying for an idle warehouse

$5,280 /month

$63,360 a year · 1,760 credits · 100% of this warehouse's billed time

At 15 queries an hour, a query arrives every 4.0 min — sooner than the 10 min auto-suspend. The warehouse never gets a gap long enough to shut down, so you are billed as if it ran continuously. It looks correctly configured on the settings page.

What other settings would cost

Auto-suspendIdle cost/monthSaved vs now
1 min$1,320$3,960
2 min$2,640$2,640
5 min$5,280—
10 min · current$5,280—
15 min$5,280—
60 min$5,280—

Don't guess the query rate — warehouse-teardown reads the real median gap straight out of your QUERY_HISTORY and tells you which warehouses never suspend. Read-only, open source, and it runs on fake data first so you can see the output before pointing it at your account.

This is one of the 18 questions in the Data Platform Teardown. The rest are published in full too.

Take the number with you.

I'll email this figure plus the free 18-check teardown — the first section is entirely about where warehouse money actually goes.

The figure with your numbers, sent at once. No confirmation step.

What breaks and what it costs — pipelines, warehouse bills, and the failures that only show up in production. A few a month, never padded to hit a schedule. No sequence, no pitch deck. Reply 'stop' once and you're off — it reaches me, not a queue.

Found something strange in your Snowflake bill?

Send me the cost export. I'll tell you what I'd investigate first — no call required. If it's interesting, I'll ask before publishing an anonymised teardown.

What to send, and what you get back — a written reply within 3 business days.

And if you would rather someone did the whole thing against your stack: the Data Platform Audit is $3,000 fixed for one week, and if day one shows no credible opportunity you pay nothing.