DEV Community

Tsubasa Kanno
Tsubasa Kanno

Posted on Edited on

Snowflake App Runtime - Deploy Full-Stack Web Apps Right Next to Your Data from a Single Prompt

⚠️ Major revision notice (September 2026)

Snowflake App Runtime has reached GA since this article was first published in June 2026, and the configuration file layout changed substantially along the way. So I rewrote this article from top to bottom in September 2026. Here's what changed:

  • Snowflake App Runtime became generally available (GA) on September 1, 2026. Everything written on the assumption of Public Preview has been revisited.
  • Configuration has consolidated into a single app.yml manifest (version: 2). The snowflake.yml that the first version centered on is now treated as legacy.
  • You can now set scale (min_instances / max_instances) and auto-suspend (auto_suspend_secs) in app.yml, so I added a dedicated section for it.
  • Deploy targets for switching between dev / stage / prod (targets / --target) are now supported, so I added a section for those too.
  • I corrected how Caller's Rights is configured. The first version said you needed a setting in app.yml; in fact the correct shape is a flag in your code combined with GRANT CALLER.
  • The snow app deploy flags, the required Snowflake CLI version, and other details that have moved on since the first version are now aligned with the current spec.

Introduction

Snowflake App Runtime, announced at Snowflake Summit 2026, reached general availability (GA) in September 2026. It's one of the features I've been keeping a close eye on since it was first announced.

Have you ever felt one of these frustrations?

  • Your data is neatly organized inside Snowflake, but the web app that surfaces it runs somewhere else entirely
  • To ship an app, you have to stand up Docker, infrastructure, and an auth layer every single time
  • You just want to build an internal tool, but you end up copying data out to the app side, and governance goes out the window

Snowflake App Runtime answers exactly these pain points. In a nutshell, it lets you deploy a full-stack web app (Next.js and friends) right next to your data — inside Snowflake itself. No infrastructure to build, authentication handled for you, and no data movement.

On top of that, when you pair it with the AI coding agent Snowflake CoCo (formerly Cortex Code), you can go from a prompt to a scaffolded app and all the way to deployment.

Note: Cortex Code was renamed CoCo at Snowflake Summit 2026, so I'll use CoCo throughout this article. That said, parts of the official documentation and some URLs still carry the Cortex Code name.

Configuration now lives in a single file: app.yml with version: 2. I'll walk through the current way to build one — starting from the overview, then the snowflake-apps skill bundled with CoCo, and on to how you write scale and auto-suspend settings.

Want to get hands-on right away?

The explanation of how things work can wait. Once you have CoCo Desktop, a recent Snowflake CLI, and Node.js 22 or later on your machine, skip ahead to Hands-On: Building a Sales Dashboard from a Prompt. Three steps — hand over a prompt, check it locally, deploy — and you land on an authenticated URL.

How to write app.yml, along with the scale and auto-suspend settings, is worth coming back to in "Basic Workflow" when you're ready to run the app in production.

Note: This article reflects my personal views and does not represent Snowflake's official position.

What is Snowflake App Runtime?

Snowflake App Runtime is a platform for building and deploying full-stack web applications on Snowflake. The official documentation describes it like this:

Snowflake App Runtime lets you go from an idea to a live, deployed web application in minutes. No infrastructure to provision, no credentials to configure, no Docker expertise required.

Here's a summary of its characteristics:

Aspect Details
Status Generally available (GA). It moved from Public Preview to GA on September 1, 2026
Supported stack Builds Node.js apps (especially Next.js). Python support is also planned
Runtime Runs as a dedicated APPLICATION SERVICE object on Snowpark Container Services (SPCS)
Authentication Snowflake SSO is applied automatically — no auth code required
Data access Directly queries data in the same Snowflake account. No data copies, no external API layer
Public URL An authenticated URL in the form https://<id>-<org>-<account>-<region>.snowflakecomputing.app/ is issued automatically
Where you can use it AWS, Azure, and Google Cloud commercial regions. Trial accounts and government regions are out of scope

Here's a point that's easy to trip over. When you hear "runs on SPCS," you might picture the SPCS services you know — the ones where you build a container yourself and run CREATE SERVICE. But App Runtime uses a different object type, APPLICATION SERVICE. The big difference is that instead of writing a service specification (spec) file yourself, it deploys from a versioned package.

The official docs spell this out:

Unlike a Snowpark Container Services service, an Application Service deploys from a versioned package rather than from a user-supplied service specification.

So when you want to check a deployed app, just remember to use SHOW APPLICATION SERVICES rather than SHOW SERVICES (I'll come back to this in the "Things to Watch Out For" section).

Architecture at a Glance

Here's the flow of how App Runtime gets an app running (this uses a Next.js project as the example, but the basic flow is the same for any Node.js app):

All the developer does is "write the app code and run snow app deploy." Building the container, placing it on SPCS, and wiring up authentication are all handled by Snowflake.

Official docs: About Snowflake App Runtime

Three Ways to Build Apps — How to Choose

There are currently three main ways to build apps on Snowflake. Plenty of people wonder "which one should I actually use?", so let's sort out where each fits.

Streamlit in Snowflake Snowflake App Runtime Native App Framework
Primary language Python JavaScript / TypeScript Any (packaged for distribution)
Best at Data exploration, dashboards, analyst self-serve Custom UI, multi-step business flows, rich web experiences Distributing/monetizing apps and data across accounts
Frontend No need to write it (handled for you) Fully customizable Fully customizable
Main use Internal analytics tools Internal web apps in general Marketplace publishing, etc.

The official docs position Streamlit and App Runtime like this:

  • Streamlit in Snowflake: Python-first and guardrailed. You can build a polished data app without writing any frontend code
  • Snowflake App Runtime: The full power of the web. Ideal when you need custom UI or rich interactions that leverage the React / Next.js ecosystem

Put simply: Streamlit when you want to visualize data quickly, App Runtime when you want a web app with elaborate UI or business workflows, and Native Apps when you want to distribute to other companies or accounts.

My past articles on Snowflake apps & UI (some details may be a bit dated)

  • Snowflake Data UI Guide - Choose the Right Tool for Every Analytics Use Case
  • SiS Container Runtime - Run Streamlit Apps at a Fraction of the Cost

One note on scope: what I build in this article is a dashboard that reads Snowflake tables and displays them. If you want a business app with frequent writes — a request form or an approval workflow, say — putting Snowflake Postgres behind it as the backend is the better fit. I cover that configuration in the follow-up article.

A Closer Look at the snowflake-apps Skill (CoCo)

This is the heart of the article. Snowflake App Runtime works on its own, but pairing it with CoCo makes the experience much smoother.

CoCo ships with a skill called snowflake-apps (a workflow definition specialized for a particular task), and it supports App Runtime app creation end to end.

Prerequisite: Setting Up CoCo Desktop

This article assumes the CoCo Desktop native app for Mac / Windows. If you haven't set it up yet, see the official documentation first:

Since you'll be running a dev server locally, things go more smoothly if you have Node.js 22 or later and a recent Snowflake CLI on your machine.

What the snowflake-apps Skill Takes Care Of

The snowflake-apps skill does more than just "scaffold an app." Following App Runtime's conventions, it sets up a foundation like this:

What the skill provides Role
Next.js template Copies a scaffold with Next.js + React + snowflake-sdk. No need to start from zero
lib/snowflake.ts A Snowflake connection helper. Just call querySnowflake() to run SQL
app.yml Auto-generates a single manifest covering the deploy destination, build commands, and display metadata (title, description, icon)
Authentication conventions Built-in patterns for switching between Owner's Rights and Caller's Rights

Accessing Data with querySnowflake()

querySnowflake(), provided by lib/snowflake.ts, is the function you use to run SQL from the app's server side (server components or API routes) and fetch data from Snowflake. The helper takes care of authentication and connection pooling, so you can fetch data by simply calling querySnowflake().

import { querySnowflake } from "@/lib/snowflake"

// Run SQL from the app's server side to fetch data
const rows = await querySnowflake("SELECT * FROM MY_DB.MY_SCHEMA.SALES")
Enter fullscreen mode Exit fullscreen mode

When you pass user input into SQL, use bind variables rather than string concatenation. This is the basic defense against SQL injection.

const rows = await querySnowflake(
  "SELECT * FROM ORDERS WHERE CUSTOMER_ID = ? AND STATUS = ?",
  { binds: [customerId, status] }
)
Enter fullscreen mode Exit fullscreen mode

For work that might run longer than a few seconds — aggregations, large scans, stored procedure calls — there's querySnowflakeLongRunning(). It submits the statement, waits for it to finish, and then returns the rows, so you can write it without worrying about timeouts.

const rows = await querySnowflakeLongRunning("CALL MY_LONG_JOB()")
Enter fullscreen mode Exit fullscreen mode

For how data access permissions are handled (the authentication mode), you have two choices. This is a spot that's easy to get confused about if you're not used to Snowflake's privilege model, so let's lay it out in a table (Owner's Rights / Caller's Rights are also concepts that come up frequently on the SnowPro certification exams!).

Mode Whose privileges run the SQL When to use
Owner's Rights (default) The app's execution role (on a standard database, the role that owns the app) Shared dashboards / reference data where all users see the same thing
Caller's Rights The signed-in user's own privileges When data should differ per user, or you want row-level security to apply

To use Caller's Rights, you just add an option to the query.

// Run SQL with the signed-in user's privileges (row-level security and so on apply)
const rows = await querySnowflake(
  "SELECT * FROM MY_DB.MY_SCHEMA.SENSITIVE_TABLE",
  { callersRights: true }
)
Enter fullscreen mode Exit fullscreen mode

Note: Caller's Rights is available automatically on an Application Service. You don't need to write any setting for it in app.yml.

That alone isn't enough to make it work, though. App Runtime's Caller's Rights sits on top of a mechanism called Restricted Caller's Rights, and data becomes readable only when both pieces are in place: the calling user's own privileges and the caller grants held by the app's execution role. So an administrator needs to issue GRANT CALLER ahead of time, like this:

GRANT CALLER USAGE ON DATABASE MY_DB TO ROLE SERVICE_OWNER_ROLE;
GRANT CALLER USAGE ON SCHEMA MY_DB.MY_SCHEMA TO ROLE SERVICE_OWNER_ROLE;
GRANT CALLER SELECT ON ALL TABLES IN SCHEMA MY_DB.MY_SCHEMA TO ROLE SERVICE_OWNER_ROLE;
Enter fullscreen mode Exit fullscreen mode

If you want tables created later to be covered as well, use GRANT INHERITED CALLER instead of GRANT CALLER. That form applies to both current and future objects.

GRANT INHERITED CALLER SELECT ON ALL TABLES IN SCHEMA MY_DB.MY_SCHEMA TO ROLE SERVICE_OWNER_ROLE;
Enter fullscreen mode Exit fullscreen mode

Note that running these GRANT CALLER statements requires the account-level MANAGE CALLER GRANTS privilege. If you hit a privilege error, that's the first place to check.

In other words, Caller's Rights is a two-part setup: pass { callersRights: true } in your code, and issue GRANT CALLER on the administrator side. Being able to choose "whose privileges this runs as" per query, while administrators keep the scope bounded, is a genuinely convenient point from a governance standpoint.

No connection string, no external auth layer. Call querySnowflake() from the server side and you can read tables in the same account. Only when you want what each user sees to differ do you need those separate GRANT CALLER statements from an administrator.

Official docs: Query Snowflake / Restricted caller's rights

The snow app CLI Command Surface

Deployment and lifecycle management are done with the Snowflake CLI's snow app command. snow app handles both Native Apps and App Runtime, and routes automatically based on your project layout.

That said, you don't need to memorize these commands one by one. Basically, CoCo gets the context from the snowflake-apps skill and runs the right commands for you, so all you have to do is say "deploy it." I'm listing the main commands here just so you understand "what's happening behind the scenes."

The main subcommands are as follows:

Command Role
snow app setup Initialize the project and generate app.yml
snow app bundle Resolve the artifacts to upload, locally (no connection required)
snow app validate Validate app.yml and confirm the deploy destination exists
snow app deploy Run upload → remote build → deploy in one shot
snow app open Open the deployed app in a browser
snow app events --last N Fetch the app's container logs
snow app teardown Remove the app and related resources

snow app deploy is made up of three phases — upload, build, and deploy — and you can run just one of them with a flag.

Flag What runs
(none) All three phases, in order
--upload-only Upload the source only
--build-only Remote build only
--promote-only Skip upload and build, and apply the manifest only

Note: When you want to skip the build and reflect only configuration changes, use --promote-only. The --deploy-only you'll see in older material is treated as a deprecated alias for the same thing. It still works, but --promote-only is the one to reach for going forward.

Official docs: Snowflake Apps CLI commands

Note: Writing your deploy configuration in app.yml requires a recent Snowflake CLI. You can check with snow helpers check-version (more on this in "Things to Watch Out For" below).

Basic Workflow

The basic flow for getting an App Runtime app up and running is these five steps.

One app.yml for Everything

It used to be a two-file arrangement: the deploy destination in snowflake.yml, and build settings alone in app.yml. Today it's all consolidated into app.yml. The key point is to put version: 2 at the top. Without that one line, the CLI won't read the deploy configuration in app.yml at all.

Running snow app setup generates this app.yml. The minimal form looks like this:

version: 2

name: SALES_DASHBOARD            # Application Service name
database: SNOWFLAKE_APPS         # destination database
schema: PUBLIC                   # destination schema
query_warehouse: MY_QUERY_WH     # warehouse the app uses for queries
Enter fullscreen mode Exit fullscreen mode

Only those five keys are required (version / name / database / schema / query_warehouse). Add display metadata and build commands on top of that, and you get something like this:

version: 2

name: SALES_DASHBOARD
database: SNOWFLAKE_APPS
schema: PUBLIC
query_warehouse: MY_QUERY_WH

# App title, description, and icon
label: Sales Dashboard
description: An app that visualizes online retail sales
icon: public/icon.svg

# Patterns excluded from upload
ignore:
  - node_modules
  - .env*
  - .next
  - .git

# Build and start commands
install:
  commands:
    - [npm, ci]
build:
  commands:
    - [npm, run, build]
run:
  command: [node, .next/standalone/server.js]
Enter fullscreen mode Exit fullscreen mode

If you omit install / build / run, they default to npm ci / npm run build / npm start respectively. You declare run explicitly when you need a start command that differs from the default — for example running Next.js with output: 'standalone'. Note that those three keys are top-level only (putting them under targets, covered below, has no effect).

Note: Title, description, and icon go in the top-level label / description / icon. The profile: block used by the older format is no longer read, and leaving it in place means the title, description, and icon disappear on every deploy. When migrating an existing project, don't forget to fix this.

One more thing: deploying with app.yml is declarative. The whole manifest is applied each time, so any field you don't write goes back to its default. That property comes up again in the next section, so it's worth keeping in the back of your mind.

Official docs: app.yml manifest for Snowflake App Runtime / Migrate from snowflake.yml to app.yml

Scale and Auto-Suspend in app.yml

Instance counts and stopping on idle are just a few lines in app.yml.

# Add instances as traffic grows
min_instances: 2
max_instances: 5

# Auto-suspend after 15 minutes of idle time, and resume when a request arrives
auto_suspend_secs: 900
auto_resume: true
Enter fullscreen mode Exit fullscreen mode

Here's what each one means, along with its default:

Setting Meaning Default Constraint
min_instances Fewest instances to run while the app is up 1 At least 1
max_instances Most instances to scale up to 1 At least min_instances, and no more than 10
auto_suspend_secs Auto-suspend after this many idle seconds 0 (disabled) If non-zero, must be 300 or more
auto_resume Whether to resume automatically when a request arrives true —

Set max_instances higher than min_instances and Snowflake adds and removes instances based on CPU utilization. It's the same CPU-based autoscaling mechanism as SPCS.

The thing to internalize here is that you're billed per node, not per instance. Multiple instances are packed onto the same node while there's room, and a new node is added only once the existing ones are full. So raising max_instances doesn't directly translate into more nodes.

Going the other way, lowering the instance count won't take you to zero. A running service keeps at least one instance, so to stop it completely you suspend it explicitly.

-- Stop it
ALTER APPLICATION SERVICE MY_DB.MY_SCHEMA.MY_APP SUSPEND;

-- Resume it
ALTER APPLICATION SERVICE MY_DB.MY_SCHEMA.MY_APP RESUME;
Enter fullscreen mode Exit fullscreen mode

Set auto_suspend_secs and you can automate that stop. Apps basically keep running once deployed, so it's worth setting for any app that has idle stretches in its day.

Note: If you change instance counts or auto-suspend with ALTER APPLICATION SERVICE, the next snow app deploy rolls that change back to what's in app.yml (or to the defaults, if you didn't write it there). Anything you want to stick permanently belongs in app.yml. This is where the declarative deploy from the previous section comes into play.

Official docs: Scale and suspend Snowflake App Runtime apps

Environment Variables, Secrets, and External Access in app.yml Too

Configuration values, credentials, and permission to reach external networks all go in app.yml as well.

# Environment variables passed to the container (non-sensitive config values)
environment_variables:
  - name: LOG_LEVEL
    value: "INFO"

# Mount a Snowflake secret object
secrets:
  - name: API_KEY
    secret: MY_DB.MY_SCHEMA.API_KEY_SECRET

# External Access Integration (EAI) for the running app's egress to external hosts
external_access_integrations:
  - my_api_integration
Enter fullscreen mode Exit fullscreen mode

A secret listed under secrets is mounted as files under /secrets/<name>/ rather than as an environment variable. Handy when you'd rather not put a password in an environment variable.

For external access, note that build time and run time use different fields.

Field When it applies What it's for
build_eai Build time npm and Google Fonts are allowed by default. Use this when the build needs additional destinations, such as a private registry
external_access_integrations Run time When the app connects to an external API or to Snowflake Postgres

Note: External access for a running app used to require attaching the integration with ALTER APPLICATION SERVICE (the follow-up article mentioned earlier shows that approach). You can now write it in app.yml under external_access_integrations.

Switching Between dev / stage / prod (Deploy Targets)

When you want to deploy the same project to multiple environments, use targets. The trick is to put shared configuration at the top level and give each target only the differences.

version: 2

# Everything below is the baseline that every target inherits
database: SNOWFLAKE_APPS
schema: PUBLIC
query_warehouse: MY_QUERY_WH
label: Sales Dashboard
icon: public/icon.svg

default_target: dev

targets:
  dev:
    name: SALES_DASHBOARD_DEV
    database: USER$              # place it in the personal database
  stage:
    name: SALES_DASHBOARD_STAGE
  prod:
    name: SALES_DASHBOARD_PROD
    query_warehouse: PROD_WH
    min_instances: 2
    max_instances: 5
Enter fullscreen mode Exit fullscreen mode

You switch with --target at deploy time. Omit it and default_target is used.

snow app deploy --target prod
Enter fullscreen mode Exit fullscreen mode

snow app open / events / teardown / validate all accept --target too, so you can keep working per environment without breaking stride.

Writing database: USER$ deploys to that user's personal database. It's convenient for experimenting during development, but an app placed in a personal database can't be shared with other roles. For an app your team will use, specify a standard database.

Official docs: Deploy targets

Hands-On: Building a Sales Dashboard from a Prompt

From here, I'll walk through how I actually built a sales dashboard app in CoCo Desktop. The subject is the sales data of a fictional online retailer, "Glacier Style" (GLACIERSTYLE_DB.EC_ANALYTICS_SCHEMA).

The app.yml covered in the previous chapter is something CoCo generates for you as part of this flow. If you started reading here, there's nothing you need to write by hand.

Step 1: Give CoCo a Prompt

In the CoCo Desktop chat, select /snowflake-apps (or use the "Build an app" starter card), then describe the app you want in natural language. Here's roughly the prompt I gave:

/snowflake-apps
Using the sales data in GLACIERSTYLE_DB.EC_ANALYTICS_SCHEMA,
build a sales dashboard that runs on Snowflake App Runtime, in Next.js.
Deploy the app itself to the GLACIERSTYLE_DB.APPS schema.

- Show KPIs (sales, order count, average order value, completion rate) as cards
- Filters to narrow by period, channel, and category
- Charts for sales by category, by channel, and the monthly trend
- Use a cool, glacier-inspired color palette
Enter fullscreen mode Exit fullscreen mode

There are two points worth highlighting here. First, the prompt explicitly invokes the /snowflake-apps skill at the top. Second, it explicitly specifies GLACIERSTYLE_DB.APPS as the deployment target. If you don't specify a target, the app may be placed in your personal database and become impossible to share with other roles — so if you have internal sharing in mind, it's best to specify it up front.

When CoCo receives this prompt, it launches the snowflake-apps skill, first runs DESCRIBE on the target tables to understand the schema, then copies the Next.js template and implements app/page.tsx, the API routes, the chart components, and the styling — all in one pass, according to your requirements.

Entering a prompt like the above in the CoCo Desktop chat

Step 2: Preview Locally

Once the implementation is done, CoCo starts a local development server (npm run dev) and checks that it works. During local development, the Snowflake connection automatically falls back to your local connection info (connections.toml), so you can verify the look and the numbers against real data before deploying.

The local preview started with  raw `npm run dev` endraw

Step 3: Deploy

Once the look and the numbers check out, it's time to deploy. Tell CoCo to "deploy it," and snow app deploy runs.

snow app deploy --verbose
Enter fullscreen mode Exit fullscreen mode

This single command automatically runs through uploading the source → building the container remotely → creating the APPLICATION SERVICE. The first build takes a little while (more on that below), and when it's done, an authenticated URL is issued.

The  raw `snow app deploy --verbose` endraw  run log

Step 4: Verify It's Running

Once deployment completes, you can access the app at a URL of the form https://<id>-<org>-<account>-<region>.snowflakecomputing.app/. This URL is protected by Snowflake SSO, so only authenticated users can use it.

You can also confirm the deployed app via SQL:

SHOW APPLICATION SERVICES IN SCHEMA GLACIERSTYLE_DB.APPS;
Enter fullscreen mode Exit fullscreen mode
name                  : GLACIER_SALES_APP
status                : RUNNING
database_name         : GLACIERSTYLE_DB
schema_name           : APPS
query_warehouse       : GLACIERSTYLE_WH
compute_pool          :
url                   : <id>-<org>-<account>-<region>.snowflakecomputing.app
auto_resume           : true
auto_suspend_secs     : 0
min_instances         : 1
max_instances         : 1
current_instances     : 1
source                : {"artifactRepository":"GLACIERSTYLE_DB.APPS.GLACIER_SALES_APP_REPO","version":"VERSION$2","alias":"LATEST","package":"GLACIER_SALES_APP"}
additional_properties : {"label":"Glacier Style Sales Dashboard","icon":"public/icon.svg"}
Enter fullscreen mode Exit fullscreen mode

The status is RUNNING, and you can see it's deployed to GLACIERSTYLE_DB.APPS exactly as specified, under version management. The min_instances / max_instances / auto_suspend_secs / auto_resume settings from the previous section show up as columns here as well. In this example auto_suspend_secs is 0, so auto-suspend is still disabled and the app stays up around the clock.

The compute_pool column is empty because App Runtime runs on a Snowflake-managed compute pool (I'll add a note on this in "Things to Watch Out For").

The running sales dashboard opened at the issued authenticated URL

Scaffold from a prompt, confirm the numbers with npm run dev, then check for RUNNING with SHOW APPLICATION SERVICES after snow app deploy. You can get all the way through that without writing a container definition file.

An aggregation SQL aside: watch for duplicates in dimension tables There's one thing to watch in your aggregation SQL too. If a dimension table contains duplicate rows for the same ID, joining it directly will inflate your counts (fanout). Deduplicate first with `QUALIFY ROW_NUMBER() OVER (PARTITION BY ORDER BY ) = 1` before joining, and you'll get correct aggregates. Checking the nature of your data is essential even when building an app.

Things to Watch Out For

Here are some points worth keeping in mind when you actually build an app — things you won't see by just skimming the official docs. Knowing them up front should help you move along smoothly without stumbling.

1. Mind the CLI version

Writing your deploy configuration in app.yml requires a recent Snowflake CLI. An older CLI silently ignores the deploy configuration in app.yml and goes looking for snowflake.yml, which shows up as the confusing symptom of "I changed the setting but nothing happened" or "it deployed with the old values." Check with snow helpers check-version and update with pip install --upgrade snowflake-cli before you start.

2. app.yml is declarative, so anything you forget to write disappears

snow app deploy applies the contents of app.yml as a whole every time. So removing a field you had written previously resets that setting to its default. Manual changes made with ALTER APPLICATION SERVICE get rolled back on the next deploy too.

The case that trips people up most is leaving an old-format profile: block in place. Since profile: is no longer read, the title, description, and icon disappear on every deploy. Move label / description / icon to the top level.

Adopt "anything I want to stick permanently goes in app.yml" as your rule and you won't get lost.

3. snow app setup needs a warehouse, and watch the deploy target

snow app setup requires a query warehouse. If you see a Missing warehouse error, pass --warehouse or set the account parameter DEFAULT_SNOWFLAKE_APPS_QUERY_WAREHOUSE.

The deploy target deserves extra care. Unlike snowflake.yml, app.yml doesn't fill in the database or schema from your connection info. If name / database / schema / query_warehouse can't be resolved from the manifest alone, the deploy fails with Missing required field(s).

Also, an app placed in a personal database with database: USER$ can't be shared with other roles, so if you have internal sharing in mind, it's best to specify a standard database and schema. State the destination in your prompt and CoCo will reflect it in this configuration for you.

4. The compute pool is Snowflake-managed, by design

App Runtime runs on an SPCS compute pool, but the pool is Snowflake-managed and fixed — you can't point it at a pool you created yourself, and app.yml has no field for specifying one.

As a result, you don't need to create a compute pool yourself, nor set up an External Access Integration for fetching npm packages (remote builds already allow npm and Google Fonts by default). Being able to start without worrying about infrastructure is a big plus. This is also why the compute_pool column is empty on a deployed app.

5. Next.js standalone builds need an explicit static asset copy

The build service used to copy .next/static and public/ into the standalone directory automatically when Next.js was set to output: 'standalone'. The current build service runs only the commands you write under build:.

So if you migrate a project that got by with just npm run build, you'll see the symptom where the app opens but CSS and images come back 404. Write the copy steps out explicitly.

build:
  commands:
    - [npm, run, build]
    - [cp, -r, .next/static, .next/standalone/.next/static]
    - [cp, -r, public, .next/standalone/public]
Enter fullscreen mode Exit fullscreen mode

If your project has no public/ directory, you don't need that last line.

6. Deployment spends more time on "endpoint provisioning" than the build

With snow app deploy, the remote build itself finishes relatively quickly, and the subsequent endpoint provisioning (Endpoints provisioning in progress...) can take a few minutes. Even if the log looks stuck on the same line, work is progressing — so wait until App ready at https://... appears. When you want to reflect a configuration-only change quickly without touching code, snow app deploy --promote-only lets you skip the upload and build.

7. Watch the connection's role during local development

During local npm run dev, querySnowflake() automatically falls back to the default connection in ~/.snowflake/connections.toml. If the connection has no role set, it runs with the default role, which can lead to a Database '...' does not exist or not authorized error. Set a role that can access the target data on the connection, or specify it via the SNOWFLAKE_ROLE environment variable.

Note that { callersRights: true } has no effect when running locally. The helper just uses your local connection and emits a warning. If you want to confirm Caller's Rights behavior, check it after deploying.

8. A deployed app doesn't show up in SHOW SERVICES

Because App Runtime runs as a separate object type, APPLICATION SERVICE, a deployed app does not appear in SHOW SERVICES. Use SHOW APPLICATION SERVICES to check it. The ordinary SPCS commands CREATE SERVICE / ALTER SERVICE don't work either, so reach for the APPLICATION SERVICE variants. The reliable way to judge a successful deploy is to look at the CLI output (App ready at https://... and the URL).

9. Stop apps you're not using

Apps basically keep running once deployed. Compute stays up even with no traffic, so an app you've stopped using is worth suspending with ALTER APPLICATION SERVICE ... SUSPEND or removing with DROP APPLICATION SERVICE. For an app that's only used during certain hours, setting auto_suspend_secs is the easy option.

You can check account-wide usage by the hour in SNOWFLAKE.ACCOUNT_USAGE.SNOWFLAKE_APP_RUNTIME_COMPUTE_HISTORY.

SELECT START_TIME, END_TIME, CREDITS_USED
FROM SNOWFLAKE.ACCOUNT_USAGE.SNOWFLAKE_APP_RUNTIME_COMPUTE_HISTORY
WHERE START_TIME >= DATEADD('day', -7, CURRENT_TIMESTAMP())
ORDER BY START_TIME DESC;
Enter fullscreen mode Exit fullscreen mode

Since every app in the account shares a single managed compute pool, what this view gives you is account-level credits. Per-app cost attribution isn't available. Billing is per node, not per instance: App Runtime packs multiple instances onto a node while there's room, and only adds a node once the existing ones are full. So you don't need to think about "how many apps fit" as a compute pool sizing question.

Cost is the only thing that's account-level. Service state, logs, and metrics are all available per app, as follows.

10. Check state, logs, and metrics per app

There's no need to go poking at the compute pool. App Runtime's observability is built around the app (APPLICATION SERVICE).

Start with state, via DESCRIBE APPLICATION SERVICE. A service reports states such as PENDING, RUNNING, SUSPENDED, and FAILED, which is how you catch the "I thought it was up, but it had died" case.

DESCRIBE APPLICATION SERVICE APP_DB.APP.MY_APP;
Enter fullscreen mode Exit fullscreen mode

For logs, you can pull the container logs directly. From SQL, use SYSTEM$GET_APPLICATION_SERVICE_LOGS: the second argument is the line count (default 500), and the third selects an instance by number. The role running it needs the MONITOR privilege on the service.

SELECT SYSTEM$GET_APPLICATION_SERVICE_LOGS('APP_DB.APP.MY_APP', 500);
Enter fullscreen mode Exit fullscreen mode

The CLI is handier still, and gives you CPU / memory / network metrics and lifecycle events on top of logs.

# Recent logs (500 lines by default; size it with --last)
snow app events

# Historical logs via the event table (works even after a suspend)
snow app events --type log --since 6h

# CPU / memory / network metrics
snow app events --type metric --metric cpu --since 1h

# Service and container state changes
snow app events --type lifecycle --since 2d
Enter fullscreen mode Exit fullscreen mode

Ask CoCo to "show me the logs for this app" and it will run these for you and walk through what they say. When a deploy fails, this is the fastest path to narrowing down the cause.

Official docs: Observability for Snowflake App Runtime

Business Value and Use Cases

The technical ease is what we've seen so far, but where App Runtime really shines is the business and organizational angle.

Value Details
Governance stays intact Because the app runs inside Snowflake's security perimeter, it inherits RBAC, audit logging, SSO, and data governance as-is
No data movement No data copies or external API layer needed, reducing the risk of data leaking outside or to the app side
Integrated authentication An authenticated URL is issued automatically — no separate login infrastructure required
Easier to build in-house Since Snowflake handles infrastructure, Docker, and server management, you can ship internal apps without creating shadow IT

Concrete use cases include:

  • Internal data visualization dashboards: Department-level KPIs delivered in an easy-to-read custom UI
  • Internal search / reference tools: Reference apps that control what each user can see via Caller's Rights
  • Entry forms and approval workflows: Business apps where requests get created and updated. For these write-heavy cases, start with the configuration that puts Snowflake Postgres behind the app

For organizations where "the data is in Snowflake, but the apps that use it rely on outsourcing or a separate platform," this looks like an option that shortens the path to building in-house.

Limitations

As handy as it is, there are some limitations to be aware of.

Note (limitations):

  • Not available on trial accounts (a paid account is required)
  • Not available in government regions. AWS, Azure, and Google Cloud commercial regions are in scope
  • Apps deployed to a personal database (USER$...) can't be shared with other roles. To share, place it in a standard database
  • Each build produces an immutable version. You can't overwrite an existing version, so every change needs a new build
  • CREATE OR REPLACE APPLICATION SERVICE isn't supported. Use CREATE OR ALTER APPLICATION SERVICE instead
  • UNDROP isn't supported, so a dropped APPLICATION SERVICE can't be brought back. That said, the artifact repository that holds your built versions (<app_name>_REPO) is a separate object from the service, so dropping the service leaves the repository in place. To bring the app back, you deploy it again
  • The supported stack centers on Node.js / Next.js (Python support is also planned, and coverage is expected to expand over time)

Please check the official documentation for the latest limitations.

Official docs: Snowflake App Runtime limitations

Conclusion

So, what did you think of Snowflake App Runtime?

Let's recap the key points of this article:

  • App Runtime is a platform that lets you deploy full-stack web apps (Next.js and friends) inside Snowflake, as an SPCS APPLICATION SERVICE. It reached GA in September 2026
  • No infrastructure, Docker, or auth implementation needed — you can publish an app while inheriting SSO and governance
  • Configuration is consolidated into a single app.yml file (version: 2), where you declaratively write the deploy destination, scale and auto-suspend, secrets, external access, and deploy targets
  • With CoCo's snowflake-apps skill, you go from prompt to scaffold to local preview to deploy in one continuous flow
  • When you actually build one, there are points worth knowing up front — the CLI version, how declarative deploys behave, specifying the deploy target, and the role during local development

The approach of "running a real web app right next to your data" looks set to make in-house app development — previously a time-consuming effort — much more approachable. Now that it's GA and the configuration side has settled, I'd expect the supported stack and features to keep expanding.

I'd love for you to get hands-on and share what you discover. Let's grow this new app-building experience together!

Promotion

App Runtime Explained on the Official Snowflake Japan YouTube Channel

The official Snowflake Japan YouTube channel has launched, and it already includes a walkthrough of Snowflake App Runtime — the very topic of this article. The video covers what App Runtime is and how to get an app deployed, with a live demo. It's in Japanese, but the demo sections should still be easy to follow alongside this article.

Snowflake What's New Updates on X

I share Snowflake What's New updates on X. Follow for the latest insights:

English Version

Snowflake What's New Bot (English Version)

Japanese Version

Snowflake's What's New Bot (Japanese Version)

Change Log

(20260605) Initial post
(20260830) Added the App Runtime walkthrough from the official Snowflake Japan YouTube channel
(20260923) Major revision. Reflected App Runtime's GA (September 1, 2026). Rewrote the configuration around the single app.yml manifest (version: 2), and added sections on scale (min_instances / max_instances) and auto-suspend (auto_suspend_secs), on environment variables / secrets / external access, and on deploy targets (targets / --target). Corrected how Caller's Rights is configured (no app.yml setting required; GRANT CALLER is) and the snow app deploy flags (--deploy-only → --promote-only). Added migration notes such as the static asset copy for Next.js standalone builds
(20260923) Added a section on per-app observability (DESCRIBE APPLICATION SERVICE, SYSTEM$GET_APPLICATION_SERVICE_LOGS, and the log / metric / lifecycle streams of snow app events). Clarified that cost is the only thing reported at the account level. Noted that when an app is dropped, its artifact repository remains as an object independent of the service

Original Japanese Article

Top comments (0)