Crosshire
Tech·Field note·9 min read·22 May 2026

A Snowflake notebook burned €70 overnight. €0.11 of it paid for work.

A Wednesday morning. The account had been quiet since lunch the day before — no job scheduled, no teammate logged in, nothing running. The meter said otherwise. €70 burned overnight, on a warehouse that, in twenty-six hours, spent six tenths of one per cent on anything a human had asked for. The cause was a single field, in a service most accounts can't see. This note is the trail back to it — and the queries that find it on your own account.

How we reproduced this

The bleed is real; the clock is ours. We let one idle managed notebook sit for 26 hours on a trial Snowflake account, then pulled the diagnostic CSVs verbatim. 35 credits burned, €70.03 at €2/credit, 99.84% unattributed to any user query. The recipe to reproduce it on your own account is in §5 (the two queries diagnose, the one ALTER NOTEBOOK fixes).

€70
35 credits · 26 hours
9,412 queries · 0.16 % to real user work
Run-rate €1,939 / month
Provenance
Account
Snowflake on AWS, eu-west-1
Burn window
2026-05-19 12:00 PDT → 2026-05-20 14:00 PDT (26 h)
Audit window
30 days · QUERY_HISTORY slice: 24 h
Sources
WAREHOUSE_METERING_HISTORY · QUERY_ATTRIBUTION_HISTORY · QUERY_HISTORY · SHOW SERVICES IN ACCOUNT
Parameters
AUTO_SUSPEND_SECS (service · cause) · IDLE_AUTO_SHUTDOWN_TIME_SECONDS (notebook · fix) · COMPUTE_POOL (notebook · backstop)
Cost basis
€2.00 per credit
Solution
Single documented lever: ALTER NOTEBOOK … SET IDLE_AUTO_SHUTDOWN_TIME_SECONDS = 900. Optional account-wide backstop: a user-owned compute pool with short AUTO_SUSPEND_SECS, bound to the notebook via ALTER NOTEBOOK … SET COMPUTE_POOL = <your_pool>.
Method
Controlled audit on a trial Snowflake account. One idle managed notebook left to sit for 26 hours to measure the bleed pattern — 35 credits burned (€70.03 at €2/credit). The mechanism is the bleed; we chose when to start the clock.
Status
Anonymised. Numbers verbatim from the diagnostic CSVs.
01The bill, as it arrived

Thirty-five credits, no one at the keyboard.

Open the meter on a Wednesday morning. Nothing had been scheduled. No teammate had signed in. No job had run. The account had been quiet since lunch the day before, and so was every dashboard a person would normally check.

And yet — between noon on the 19th and 14:00 the next day, COMPUTE_WH burned 35.015 credits. At €2 a credit: €70.03. The same warehouse, left running at the same rate, was on track for €1,939 a month. The only thing it was paying for was the act of being awake.

Of all the credits COMPUTE_WH spent in thirty days, 79.83 % were spent inside this one 26-hour window.

The shape on the meter is unmistakable. Across the rest of the thirty-day pull — every other captured hour, every benchmark, every demo, every diagnostic query that produced this analysis — the warehouse spent 8.85 credits in total. The bleed window alone is four times that. Read the chart below as a tide chart: a normal account does its work in spikes; this account has a single waist-high wall of credits with everything else rounding to the floor.

COMPUTE_WH credits, hourly 30-day window · €2 / credit
0 0.5 1.0 1.5 cr/h 26-hour bleed window May 19 12:00 PDT → May 20 14:00 PDT Apr 20 May 1 May 15 ↓ service suspended
One bar per hour. The block on the right is the 26-hour stretch where the notebook held COMPUTE_WH warm; the scatter to its left is every other hour in the 30-day pull. Dashed line marks when the service was finally suspended.
Reproducibility note
  • This is a controlled reproduction on one quiet trial account — the absolute euros are small by design, so the method can be shown in public without exposing a customer's bill.
  • The per-hour rate (~1.35 cr / hour on COMPUTE_WH), the idle share (99.84 %), and the default AUTO_SUSPEND_SECS (0 on the managed SPCS notebook service) are what scale to a production account.
  • A single warehouse held warm at the same rate is ~€1,939 / month. Five such notebooks is ~€9,700. Twenty is ~€38,800. Multiply by your warehouse size and credit price from there.
02Where the credits actually went

Of thirty-five credits, six hundredths did real work.

The first place to look isn't the bill — it's QUERY_ATTRIBUTION_HISTORY. The table answers the question the meter doesn't: of every credit a warehouse spent, which query was responsible? Run it over the burn window and the answer is almost comically lopsided.

COMPUTE_WH · burn window · 327 attribution rows
Bucket Credits EUR Share
Attributed to real user queries on COMPUTE_WH 0.0558 €0.11 0.16 %
Attributed to SYSTEM user (background) 0.0004 €0.00 0.001 %
Diagnostic queries on a different warehouse (out of scope) 0.2288 €0.46 n/a
Unattributed COMPUTE_WH time = idle keep-warm ~34.96 €69.92 ~99.84 %
COMPUTE_WH metered in window 35.015 €70.03 100 %
Of the €70.03 that COMPUTE_WH burned in twenty-six hours, €0.11 — eleven euro cents — went to attributable user query work. The rest was idle warehouse-seconds held open by a heartbeat.

The rest of the account, that same day, spent €1.64 doing real diagnostic and demo work across four other warehouses. The bleed is concentrated. One warehouse, one service holding it open, and twenty-six hours of paying to keep the lights on.

03Nine thousand queries, nobody home

The query feed reads like a clock ticking.

If eleven cents paid for queries, what filled the other twenty-six hours? Sort QUERY_HISTORY for the same window and the picture clears up further. Nine thousand four hundred and twelve rows. The top four patterns own 57.4 % of every query, and not one of them was typed by a human:

QUERY_HISTORY · 24h · top patterns · n = 9,412
Count % total Pattern (first 80 chars)
1,35414.39 %LIST 'snow://workspace/USER$…/versions/head/'
1,34914.33 %LIST 'snow://workspace/USER$…/versions/head/.sidecar_health_probe'
1,34914.33 %GET  'snow://workspace/USER$…/versions/head/.sidecar_health_probe/'
1,34914.33 %GET  'snow://workspace/USER$…/versions/head/.sidecar_health_probe'
1,27013.49 %call SYSTEM$NOTEBOOK_CONTAINER_PARAMS(?)
4374.64 %SHOW SERVICE CONTAINERS IN SERVICE IDENTIFIER('USER$…')
4374.64 %SHOW ENDPOINTS LIKE 'snowflake-notebook' IN SERVICE USER$…
4374.64 %CALL SYSTEM$GENERATE_SERVICE_TOKEN(?, ?)

Read the cadence back as a clock. Once every sixty-four seconds, a sidecar health probe. Every sixty-eight seconds, a container-params ping. Every three minutes and eighteen seconds, a fresh service token. Stack the timers together and the workspace is firing a query every six or seven seconds, around the clock, all of them small, none of them yours.

That cadence is the whole mechanism. Each query takes a few milliseconds and a sliver of a credit. What they do, together, is reset COMPUTE_WH's auto-suspend countdown every minute, before it gets the chance to expire. The warehouse never goes to sleep, because something on the other end keeps tapping it awake.

04The setting that did it

One field, set to zero.

All of it traces back to a single field. The notebook service that Snowsight provisions to back a workspace looked like this the whole time:

DESC SERVICE · <account>_SERVICE_1
StatusRUNNING (never suspended)
auto_suspend_secs0  - auto-suspend disabled
auto_resumefalse
min_instances / max_instances1 / 1 — single container, pinned
Compute poolSYSTEM_COMPUTE_POOL_CPU
Query warehouseCOMPUTE_WH (account default)
Database / schemaUSER$<account>.PUBLIC

auto_suspend_secs = 0 is the field. It means the service never auto-suspends on its own. min_instances = 1 means at least one SPCS container is always running. The container fires its health probe at the bound warehouse every minute. The warehouse, in turn, resets its own auto-suspend timer on every query. The two timers loop. The loop has no exit.

COMPUTE_WH was at 600 seconds - Snowflake's default ten-minute auto-suspend. The reflex, faced with a warm warehouse, is to shorten that. It would not have helped. The sidecar health probe alone fires every ~64 seconds; stack the container-params ping and the token refresh on top and the workspace was sending a query every ~9 seconds on average. Drop the auto-suspend to 5 minutes, to 2 minutes, all the way down to the 60-second minimum Snowflake will let you set - and the very next heartbeat re-wakes the warehouse before the timer can expire. The timer never reaches the bottom, because something keeps tapping it back to the top.

COMPUTE_WH · auto_suspend · effect on the 26-hour burn
Warehouse AUTO_SUSPEND 26-hour burn Why
60 s (Snowflake minimum) ~€70 Heartbeat fires inside the window; warehouse never suspends
120 s (2 min) ~€70 Same
300 s (5 min) ~€70 Same
600 s (10 min, Snowflake default · this account) €70.03 The bleed, as observed
Service suspended ~€0 Removes the heartbeat at the source

A warehouse cannot auto-suspend out from under a notebook that is pinging it. The lever is upstream. The fix is at the notebook, not the warehouse.

What the docs say
  • AUTO_SUSPEND_SECS = 0 is the documented default for SPCS services — not an oversight, not a misconfiguration. Per the ALTER SERVICE reference: “When AUTO_SUSPEND_SECS is 0 (default), Snowflake does not auto-suspend the service.”
  • The minimum non-zero value is 300 seconds (5 minutes). Anything between 1 and 299 is rejected.
  • It is still a Preview feature on SPCS. The ALTER SERVICE page is explicit: “Configuring the automatic suspension of a Snowpark Container Services service using the AUTO_SUSPEND_SECS property is a preview feature.”
  • Workspace notebook services default to a 24-hour idle timeout on the notebook object itself. The metering on this account confirms it: the hourly rate begins tapering at 13:00 on May 20 — consistent with the 24-hour idle tick firing at noon — and the service is fully quiet by 15:00.
  • Snowflake's own setup docs recommend dropping the idle timeout. Verbatim from the notebooks setup page: “minimize the idle timeout setting (for example, to 15 minutes) if they do not need the session to run for an extended period of time.” ALTER NOTEBOOK … SET IDLE_AUTO_SHUTDOWN_TIME_SECONDS = 900 is the GA, single-parameter fix; the floor is 60 s.
  • Compute-pool default differs from the service default. Per CREATE COMPUTE POOL: “Number of seconds of inactivity after which you want Snowflake to automatically suspend the compute pool … Default: 3600 seconds” (1 hour) - not 0. The two managed pools (SYSTEM_COMPUTE_POOL_CPU and SYSTEM_COMPUTE_POOL_GPU) are Snowflake-provided and scoped to specific workloads; for an account-wide backstop, create your own pool with a shorter value and point notebooks at it via ALTER NOTEBOOK … SET COMPUTE_POOL. The required privilege on a user pool is MODIFY (or OWNERSHIP).

Why it ends up at zero. Snowsight provisions notebook services as SYSTEM$MANAGED - the OWNER_ROLE on the service is NULL because Snowflake manages them on the user's behalf. The intended lifecycle is the notebook's own idle timeout, set on the notebook object itself. Close the notebook in Snowsight or hit the timeout, and the service suspends. Close the browser tab without triggering either, and with AUTO_SUSPEND_SECS = 0 as the documented default there is nothing else to stop it.

And the service itself is not anywhere most people would look. It lives in a database called USER$<your-account-name> — an auto-provisioned per-user workspace that doesn't appear in SHOW DATABASES for regular roles and isn't in Snowsight's database tree. From every normal view of the account, the service is invisible. It only surfaces from the very top:

Surface the invisible serviceSHOW SERVICES IN ACCOUNTsql
-- run as ACCOUNTADMIN; this is the only place a workspace notebook surfaces
SHOW SERVICES IN ACCOUNT;

-- pick the row, then dig in:
DESC SERVICE USER$<account>.PUBLIC.<service_name>;
-- key columns: auto_suspend_secs, auto_resume, min_instances, query_warehouse, status

And the patch, once you've found it. The Snowflake-documented lever is the notebook's own idle timeout - IDLE_AUTO_SHUTDOWN_TIME_SECONDS, GA, 60 s floor, 24 h default. The setup docs explicitly recommend minimising it (15 min is the example they give). An optional account-wide backstop is to bind notebooks to a user-owned compute pool with its own short AUTO_SUSPEND_SECS; you cannot ALTER SYSTEM_COMPUTE_POOL_CPU directly because it is Snowflake-managed.

Stop the bleedALTER NOTEBOOK · CREATE COMPUTE POOLsql
-- 1. Preferred: shorten the notebook's own idle timeout.
--    Workspace notebook services default to 24 h; 15 min is sensible.
ALTER NOTEBOOK <db>.<schema>.<notebook>
  SET IDLE_AUTO_SHUTDOWN_TIME_SECONDS = 900;

-- 2. Account-wide backstop (optional): your own compute pool with a
--    short AUTO_SUSPEND_SECS, then point the notebook at it. The two
--    SYSTEM_COMPUTE_POOL_* pools are Snowflake-managed and scoped to
--    specific workloads - create your own for a per-account default.
--    CPU_X64_XS is the documented smallest CPU instance family.
--    AUTO_RESUME defaults to TRUE.
CREATE COMPUTE POOL my_notebook_pool
  MIN_NODES = 1
  MAX_NODES = 1
  INSTANCE_FAMILY = CPU_X64_XS
  AUTO_SUSPEND_SECS = 900;

ALTER NOTEBOOK <db>.<schema>.<notebook>
  SET COMPUTE_POOL = my_notebook_pool;

One honest caveat: because the service is SYSTEM$MANAGED, properties set with ALTER SERVICE can be reset when the notebook is next opened in Snowsight. That is why the notebook-object lever (IDLE_AUTO_SHUTDOWN_TIME_SECONDS) is the durable one — it sits on the notebook itself, not on the managed service.

05Check your own account

Diagnose first, then fix.

The notebook is one cause. It isn't the only one. A warehouse held warm against your intent has a small family of explanations — and the same two queries surface all of them. Run the diagnosis first. Decide which fix applies once you can see the shape of your own meter.

What the meter shows

Two queries, runnable in a worksheet, no audit tool required. The first asks how many credits your warehouses actually burned. The second asks how many of those credits were attributed to a query a human or system ran. The gap between the two is the part of your bill that isn't paying for queries.

1 · What the meter saysWAREHOUSE_METERING_HISTORYsql
SELECT  warehouse_name,
        SUM(credits_used)     AS credits,
        SUM(credits_used) * 2 AS cost_eur
FROM    snowflake.account_usage.warehouse_metering_history
WHERE   start_time >= DATEADD(day, -7, CURRENT_TIMESTAMP())
GROUP BY 1
ORDER BY credits DESC;
2 · What real work the meter paid forQUERY_ATTRIBUTION_HISTORYsql
SELECT  warehouse_name,
        user_name,
        SUM(credits_attributed_compute) AS attributed_credits,
        COUNT(*)                         AS query_count
FROM    snowflake.account_usage.query_attribution_history
WHERE   start_time >= DATEADD(day, -7, CURRENT_TIMESTAMP())
GROUP BY 1, 2
ORDER BY attributed_credits DESC;

Subtract the second result from the first, per warehouse. A small gap is normal — warehouses warm up; an idle tail before suspend is unavoidable. A large gap is a finding. On the account in this note, the gap on COMPUTE_WH was 99.84 %: of every euro spent, ninety-nine cents were not paying for a query.

What is holding the warehouse warm?

Once you have a gap, the next question is what's keeping the warehouse awake. A managed notebook is the cause in this audit. In other accounts we see four other patterns just as often:

Common causes of a wide meter-to-work gap
Pattern What it looks like Where to look
Managed notebook with auto_suspend_secs = 0 (this audit) Sidecar health probe every ~60 s against the bound warehouse SHOW SERVICES IN ACCOUNT
Scheduled task on a short interval A task fires every minute or two; resets the warehouse's auto-suspend each time SHOW TASKS IN ACCOUNT · TASK_HISTORY
BI tool with a live connection Tableau / Looker / Sigma keep-alive pings, dashboard auto-refresh on a minute timer QUERY_HISTORY filtered by CLIENT_APPLICATION_ID
Driver session pool left open Application server holds long-lived sessions; JDBC/ODBC pool refreshes on a timer SESSIONS view · LOGIN_HISTORY by CLIENT_APPLICATION
Third-party SaaS connector polling Reverse-ETL / observability / lineage tool calling out on a fixed cadence QUERY_HISTORY by USER_NAME ending in _SVC / _BOT
3 · Surface the candidatesSERVICES · TASKS · QUERY_HISTORYsql
-- a) Notebook / SPCS services. Run as ACCOUNTADMIN -
--    workspace services live in USER$<name> databases hidden from SHOW DATABASES.
SHOW SERVICES IN ACCOUNT;

-- For any row, read these columns: auto_suspend_secs, min_instances, query_warehouse.
DESC SERVICE USER$<account>.PUBLIC.<service_name>;

-- b) Scheduled tasks that may be ticking too often.
SHOW TASKS IN ACCOUNT;

-- c) The query-pattern shape that gives away a heartbeat.
SELECT  client_application_id,
        user_name,
        LEFT(REGEXP_REPLACE(query_text, '\s+', ' '), 80) AS pattern,
        COUNT(*) AS cnt
FROM    snowflake.account_usage.query_history
WHERE   start_time >= DATEADD(hour, -24, CURRENT_TIMESTAMP())
GROUP BY 1, 2, 3
HAVING COUNT(*) > 100
ORDER BY cnt DESC;

A pattern that fires hundreds or thousands of times in a day, from the same client and the same user, with the same first eighty characters, is a heartbeat. The candidates name themselves.

The fix, by pattern

If the culprit is a managed notebook service, this is the immediate fix. Run as ACCOUNTADMIN against the service surfaced in step 3a:

4 · Stop the bleedALTER NOTEBOOK · IDLE_AUTO_SHUTDOWN_TIME_SECONDSsql
-- Documented lever. Snowflake's own notebook-setup docs recommend
-- minimising the idle timeout (15 min is sensible). 60 s floor; 24 h default.
ALTER NOTEBOOK <db>.<schema>.<notebook>
  SET IDLE_AUTO_SHUTDOWN_TIME_SECONDS = 900;
Fix, by pattern
Pattern Lever
Managed notebook ALTER NOTEBOOK … SET IDLE_AUTO_SHUTDOWN_TIME_SECONDS = 900
Scheduled task Raise the SCHEDULE interval; consolidate small tasks; suspend overnight
BI keep-alive Disable connection keep-alive at the BI tool; move dashboards to extracts where possible
Driver session pool Cap pool size; set a short idle-timeout in the application; close pool on shutdown
SaaS connector polling Lengthen the poll interval; switch to event-driven where the tool supports it
A heartbeat is not activity. A warehouse that is kept warm by something that isn't you is a bill you didn't ask for. — Field note, §5
From our audit
We diagnosed this on an account with no production workload and no scheduled jobs. On every Snowflake account we have audited so far, at least one of these patterns has been present. A Crosshire Snowflake audit is a two-week engagement: the rules engine runs 36 checks against your account, the discovery pass flags reframings like this one, and a human reviews every finding before it reaches the report. You keep the queries, the findings, and the runbook - whether or not you commission what comes next.
Start a conversation →
Sources
· · ·

Numbers in this note are from a controlled audit we ran on a trial Snowflake account - one idle managed notebook left to sit for 26 hours to measure the bleed pattern. 35 credits burned, real, anonymised so the method can be shown in public. The diagnostic checks described here ship in every Crosshire audit. — Crosshire

D
Darshan Singh
writes Crosshire Journal · crosshire.ch · May 2026
Crosshire Journal
Field reports on data, compute, and the unglamorous decisions that shape engineering teams. Made in EU. Cited evidence, GDPR-native.

Cite this article

Singh, D. (2026). A Snowflake notebook burned €70 overnight. €0.11 of it paid for work. Crosshire Journal. Retrieved from https://blogs.crosshire.ch/blogs/snowflake-notebook-bleed
© 2026 Crosshire Journal · Made in EU Privacy Terms Imprint