Snowflake Notebooks stopped being a toy the moment two things landed: a Container Runtime that runs arbitrary pip packages (and GPUs) inside your account, and Git integration that lets a notebook be a file in a repository instead of a blob in a metadata table. Together they turn the notebook from a scratchpad into something you can review, deploy, and schedule.
This tutorial covers the decisions teams get wrong: which runtime to pick, how to keep notebooks in source control, how to run one on a schedule with parameters, and how to stop notebook compute quietly becoming a line item nobody owns.
The two runtimes
| Warehouse runtime | Container Runtime (on Snowpark Container Services) | |
|---|---|---|
| Compute | A virtual warehouse | A compute pool (CPU or GPU nodes) |
| Python packages | Snowflake Anaconda channel only | Anaconda plus anything pip can install from PyPI |
| GPU | No | Yes (GPU node families) |
| Best for | SQL-heavy exploration, Snowpark DataFrame work, light pandas | ML training, open-source libraries, model fine-tuning, long-running Python |
| Billing | Warehouse credits while the warehouse is active | Compute pool credits while nodes are up, plus a small warehouse for SQL |
| Start latency | Warehouse resume (seconds) | Compute pool node start (tens of seconds to minutes if cold) |
Rule of thumb: if the notebook is mostly SQL and Snowpark push-down, use the warehouse runtime. The moment you need xgboost from PyPI, a Hugging Face model, or a GPU, switch to Container Runtime. Do not use Container Runtime as the default — a warm compute pool costs credits even when the notebook is idle.
One detail that catches people out: in the warehouse runtime, the notebook's kernel and its SQL both use the same warehouse. In Container Runtime, Python runs on the compute pool and SQL cells still route to a warehouse, so you configure both.
Creating a notebook from SQL (not just the UI)
Notebooks are schema-level objects, so they can be created, granted, and cloned like anything else:
CREATE OR REPLACE NOTEBOOK analytics.ds.churn_features
FROM '@analytics.ds.notebook_stage/churn/'
MAIN_FILE = 'churn_features.ipynb'
QUERY_WAREHOUSE = wh_notebook_xs
COMMENT = 'Feature build for churn model';
ALTER NOTEBOOK analytics.ds.churn_features ADD LIVE VERSION FROM LAST;
GRANT USAGE ON NOTEBOOK analytics.ds.churn_features TO ROLE ds_analyst;
For Container Runtime you attach a compute pool instead of relying on the warehouse for Python:
CREATE COMPUTE POOL IF NOT EXISTS ds_gpu_pool
MIN_NODES = 1 MAX_NODES = 2
INSTANCE_FAMILY = GPU_NV_S
AUTO_SUSPEND_SECS = 300;
CREATE OR REPLACE NOTEBOOK analytics.ds.churn_train
FROM '@analytics.ds.notebook_stage/churn/'
MAIN_FILE = 'churn_train.ipynb'
QUERY_WAREHOUSE = wh_notebook_xs
COMPUTE_POOL = ds_gpu_pool
RUNTIME_NAME = 'SYSTEM$GPU_RUNTIME';
AUTO_SUSPEND_SECS on the pool is the single most important cost setting in this whole tutorial. Set it. Five minutes is a sane default for interactive work.
Put the notebook in Git
A notebook you cannot diff is a notebook you cannot review. Wire a Git repository into the account once:
CREATE OR REPLACE SECRET analytics.ds.github_pat
TYPE = PASSWORD
USERNAME = 'svc-snowflake'
PASSWORD = '<fine-grained PAT>';
CREATE OR REPLACE API INTEGRATION github_api
API_PROVIDER = git_https_api
API_ALLOWED_PREFIXES = ('https://github.com/acme-data')
ALLOWED_AUTHENTICATION_SECRETS = (analytics.ds.github_pat)
ENABLED = TRUE;
CREATE OR REPLACE GIT REPOSITORY analytics.ds.ds_repo
API_INTEGRATION = github_api
GIT_CREDENTIALS = analytics.ds.github_pat
ORIGIN = 'https://github.com/acme-data/ds-notebooks.git';
ALTER GIT REPOSITORY analytics.ds.ds_repo FETCH;
Now a notebook can be created directly from a branch, and promotion between environments is a FETCH plus a re-create:
CREATE OR REPLACE NOTEBOOK analytics.ds.churn_features
FROM '@analytics.ds.ds_repo/branches/main/churn/'
MAIN_FILE = 'churn_features.ipynb'
QUERY_WAREHOUSE = wh_notebook_xs;
Two practical habits make notebook diffs readable:
- Clear outputs before committing. Base64 plot images make every diff useless. A
nbstripoutpre-commit hook fixes this permanently. - Keep notebooks thin. Import shared logic from
.pyfiles in the same repo (stage imports work in both runtimes) so the reviewable code lives in modules and the notebook is orchestration plus narrative.
Parameterise, then schedule
A notebook destined for a schedule must not hard-code a date, a database, or a role. Read them from the session:
from snowflake.snowpark.context import get_active_session
session = get_active_session()
env = session.sql("SELECT SYSTEM$GET_PREDECESSOR_RETURN_VALUE()").collect() if False else None
run_date = session.sql("SELECT CURRENT_DATE()").collect()[0][0]
target_db = session.get_current_database()
print(f"Building features for {run_date} in {target_db}")
For real parameters, set session variables from the calling task and read them with SELECT $my_var or SYSTEM$GET_TASK_GRAPH_CONFIG. Then schedule the notebook as a task:
CREATE OR REPLACE TASK analytics.ds.t_churn_features
WAREHOUSE = wh_batch_m
SCHEDULE = 'USING CRON 30 5 * * * UTC'
CONFIG = '{"lookback_days": 90}'
AS
EXECUTE NOTEBOOK analytics.ds.churn_features();
ALTER TASK analytics.ds.t_churn_features RESUME;
EXECUTE NOTEBOOK runs every cell top to bottom and fails the task on the first error — which is exactly why cell order discipline matters. A notebook that only works when you run cell 7 before cell 4 will pass interactively and fail at 05:30.
Chain it into a larger graph with AFTER, and monitor it like any other task:
SELECT name, scheduled_time, state, error_message
FROM TABLE(analytics.INFORMATION_SCHEMA.TASK_HISTORY(
TASK_NAME => 'T_CHURN_FEATURES', RESULT_LIMIT => 20))
ORDER BY scheduled_time DESC;
Cost control: the four settings that matter
Notebook spend hides well because it is spread across a warehouse, a compute pool, and occasionally a GPU node that someone left running over a weekend.
- A dedicated XS warehouse for notebooks (
wh_notebook_xs) withAUTO_SUSPEND = 60. Never point notebooks at the BI warehouse — you lose all attribution. AUTO_SUSPEND_SECSon every compute pool, plusMAX_NODESlow enough that a runaway job cannot scale into four figures.- Notebook idle timeout. Snowflake suspends idle notebook sessions; keep the account default short rather than extending it per notebook.
- A budget and a resource monitor scoped to the notebook warehouse and pool, so overspend produces an email rather than a quarterly surprise.
Attribution query worth saving:
SELECT
DATE_TRUNC('day', start_time) AS d,
'warehouse: ' || warehouse_name AS source,
SUM(credits_used) AS credits
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE warehouse_name ILIKE 'WH_NOTEBOOK%' AND start_time >= DATEADD(day, -30, CURRENT_DATE())
GROUP BY 1, 2
UNION ALL
SELECT
DATE_TRUNC('day', start_time),
'pool: ' || compute_pool_name,
SUM(credits_used)
FROM SNOWFLAKE.ACCOUNT_USAGE.SNOWPARK_CONTAINER_SERVICES_HISTORY
WHERE start_time >= DATEADD(day, -30, CURRENT_DATE())
GROUP BY 1, 2
ORDER BY d DESC, credits DESC;
Governance notes
- Notebooks execute with the owner's rights when run by a task, and with the caller's role interactively. Give the task a purpose-built role with the narrowest grants, not
ACCOUNTADMIN. - External package installs in Container Runtime go through an external access integration with a network rule pointing at PyPI. If your account blocks egress by default (and it should), nothing installs until that integration exists and is granted.
- Pin versions.
requirements.txtin the repo, notpip installof whatever is latest, or a working notebook silently breaks on the next cold start. - Treat a scheduled notebook as production code: PR review, a dev-account run, then promotion from
main.
When not to use a notebook
A notebook is a good home for exploration, feature engineering that benefits from inline charts, model training, and analyst-facing runbooks. It is a poor home for a core ELT pipeline — that belongs in dbt models or stored procedures driven by task graphs, where lineage, testing, and retries are first-class. The cleanest architecture we see in client accounts: notebooks for the ML and investigation layer, declarative pipelines for everything that a dashboard depends on.
Rollout checklist
- Dedicated notebook warehouse,
AUTO_SUSPEND = 60 - Compute pools created per workload class with
AUTO_SUSPEND_SECSandMAX_NODESset - Git repository object wired up;
nbstripouton the repo - Shared logic in
.pymodules, notebooks thin - Every scheduled notebook runs clean top-to-bottom under
EXECUTE NOTEBOOK - Task-owner role scoped, external access integration explicit
- Budget and resource monitor covering notebook compute
PowderInsights builds and reviews Snowflake data science platforms — runtime selection, compute pool sizing, Git-backed notebook workflows, and the cost guardrails around them. Get in touch with your use case and we will tell you what it should cost to run.