Optional app.yml manifest for Snowflake App Runtime

The optional app.yml manifest tells Snowflake how to install, build, run, and package your application, along with optional display metadata (profile) and runtime configuration such as environment variables, secrets, and external access integrations.

When you don’t provide it, Snowflake auto-detects build and run behavior from your project layout (for example, package.json for Node.js). Hand-edit app.yml when you need to customize those install, build, run, or packaging steps, or to set the app’s display profile, environment variables, secrets, or external access integrations. Name the file app.yml and place it in your application source.

Note

Cortex Code CLI and Cortex Code Desktop generate this file automatically when scaffolding Snowflake App Runtime projects.

For deploy configuration, the Snowflake CLI uses a separate snowflake.yml project file that snow app setup creates and snow app deploy reads. See Getting started with Snowflake App Runtime and snow app setup.

app.yml reference

Top-level structure

install:
  commands:
    - [<command>, <arg>, ...]
build:
  commands:
    - [<command>, <arg>, ...]
run:
  command: [<command>, <arg>, ...]
artifacts:
  - src: <glob_pattern>
    dest: <destination_path>
profile:
  icon: <relative_path_to_icon>
  label: <display_label>
  description: <description_text>
environment_variables:
  - name: <var_name>
    value: <var_value>
secrets:
  - name: <mount_name>
    secret: <fully_qualified_secret_name>
external_access_integrations:
  - <integration_name>
min_instances: <num>
max_instances: <num>

All top-level keys are optional. Snowflake ignores unknown keys.

Keys

install

Commands Snowflake runs during the install phase, before the build. Each entry under commands is an argv-style list, passed directly without a shell. Use install for setup steps such as installing dependencies.

install:
  commands:
    - [npm, ci]

build

Commands Snowflake runs during the build phase. Each entry under commands is an argv-style list.

build:
  commands:
    - [npm, run, build]

run

The entry-point command that starts the application container. command is an argv-style list.

run:
  command: [node, server.js]

artifacts

Copy rules that move files produced by the build into the final package. Each entry has these fields:

  • src: Glob pattern, relative to the build working directory, that matches the files to copy. The pattern supports ** for recursive matching (provided by the doublestar library). For example, dist/** matches every file under dist at any depth.
  • dest: Destination path within the package. Set dest to . to flatten matched files into the package root by basename. Set dest to any other path to preserve the directory structure of the matched files relative to the glob base, rooted at dest.

Only files that match the glob are copied; directories themselves aren’t copied. To include a directory tree, use **.

artifacts:
  - src: "dist/**"
    dest: "."
  - src: "package.json"
    dest: "."

profile

Presentation metadata surfaced alongside the deployed service.

  • icon: Relative path to a .png, .svg, or .webp file inside the package. The path must not contain .., and it can’t be an absolute path.
  • label: Short display label.
  • description: Longer human-readable description.

The label and description values appear in the additional_properties column of SHOW APPLICATION SERVICES.

profile:
  icon: assets/logo.png
  label: Customer Portal
  description: Web UI for the customer-facing portal.

environment_variables

Non-sensitive configuration values that Snowflake exposes as environment variables inside the application container. Each entry has:

  • name: Environment variable name. Must match the POSIX pattern [A-Z_][A-Z0-9_]* (uppercase letters, digits, and underscores; must start with a letter or underscore). Duplicate names aren’t allowed.
  • value: String value.

Some environment variable names are reserved for the platform and can’t be set here: any name starting with SF_SNOWFS_, plus SNOWFLAKE_AUTH_MODE, SNOWFLAKE_INSECURE_MODE, NODE_ENV, NEXT_PRIVATE_STANDALONE, PORT, and HOSTNAME. Setting a reserved name fails validation or is dropped at deploy.

environment_variables:
  - name: LOG_LEVEL
    value: "INFO"
  - name: APP_REGION
    value: "us-west-2"

Read them like any other environment variable in your application code:

const logLevel = process.env.LOG_LEVEL ?? "INFO";
import os
log_level = os.getenv("LOG_LEVEL", "INFO")

secrets

Snowflake secret objects to make available to the application at runtime. Each entry has:

  • name: Mount name used to locate the secret files inside the container. Must match the POSIX pattern [A-Z_][A-Z0-9_]*. Duplicate names aren’t allowed.
  • secret: Fully qualified name of an existing Snowflake secret (<database>.<schema>.<secret_name>).
secrets:
  - name: API_KEY
    secret: db.schema.api_key_secret

Snowflake mounts each secret as one or more files under /secrets/<name>/. The application reads secret values from these files. File-based delivery lets Snowflake rotate a secret while the container runs; the file content updates in place.

The files written depend on the secret type:

Secret typeFiles under /secrets/<name>/
Generic stringsecret_string
Passwordusername, password
OAuth2access_token

Symmetric key and cloud provider secret types aren’t supported.

OAuth2 secrets require an external access integration: the secret must be listed in the integration’s ALLOWED_AUTHENTICATION_SECRETS, and the integration must be attached to the app either through the external_access_integrations section of this manifest or through EXTERNAL_ACCESS_INTEGRATIONS on CREATE APPLICATION SERVICE. Password and generic string secrets don’t require an external access integration.

Read a generic string secret:

import { readFileSync } from "node:fs";
const apiKey = readFileSync("/secrets/API_KEY/secret_string", "utf8").trim();
with open("/secrets/API_KEY/secret_string") as f:
    api_key = f.read().strip()

Read a password secret:

import { readFileSync } from "node:fs";
const username = readFileSync("/secrets/DB_CREDENTIALS/username", "utf8").trim();
const password = readFileSync("/secrets/DB_CREDENTIALS/password", "utf8").trim();
with open("/secrets/DB_CREDENTIALS/username") as f:
    username = f.read().strip()
with open("/secrets/DB_CREDENTIALS/password") as f:
    password = f.read().strip()

Read an OAuth2 access token:

import { readFileSync } from "node:fs";
const oauthToken = readFileSync("/secrets/OAUTH_TOKEN/access_token", "utf8").trim();
with open("/secrets/OAUTH_TOKEN/access_token") as f:
    oauth_token = f.read().strip()

external_access_integrations

Names of external access integrations that the app service uses to reach external hosts (for example, third-party APIs or Snowflake Postgres instances). External access integrations are account-level objects, so each entry is the name of an existing integration.

external_access_integrations:
  - my_api_integration
  - my_slack_integration

To see the integrations currently attached to a deployed app service, run DESCRIBE APPLICATION SERVICE.

min_instances

Minimum number of instances Snowflake keeps running. Snowflake never runs fewer than this, even if the app is idle. Equivalent to setting MIN_INSTANCES in CREATE APPLICATION SERVICE.

  • Must be at least 1.
  • Can’t exceed max_instances.
min_instances: 2

max_instances

Maximum number of instances Snowflake runs. Snowflake never runs more than this. Equivalent to setting MAX_INSTANCES in CREATE APPLICATION SERVICE.

  • Must be at least 1 and no more than 10.
  • Can’t be less than min_instances.
max_instances: 5

Validation during the build phase

When your project includes app.yml and you run snow app deploy, Snowflake loads and validates the manifest from uploaded source during the build phase. If the file is missing, Snowflake can still auto-detect build and run behavior from your project layout.

If app.yml is present but invalid, the build fails before a new package version is published. Common causes include:

  • YAML syntax errors.
  • Invalid profile.icon values (path traversal, absolute paths, or unsupported image formats).

Names in secrets and external_access_integrations are resolved when Snowflake creates or upgrades the Application Service. An unresolvable name fails that create or upgrade step, not the build phase.

The error message includes parser details, for example:

Application service manifest (app.yaml) cannot be parsed. '<details>'.

Example

install:
  commands:
    - [npm, ci]
build:
  commands:
    - [npm, run, build]
run:
  command: [node, dist/server.js]
artifacts:
  - src: "dist/**"
    dest: "."
profile:
  icon: assets/logo.png
  label: Customer Portal
  description: Customer portal web UI.
environment_variables:
  - name: LOG_LEVEL
    value: "INFO"
secrets:
  - name: API_KEY
    secret: db.schema.api_key_secret
external_access_integrations:
  - my_api_integration