Sign inSign up

localstack/snowflake-next

Verified Publisher

By LocalStack GmbH

•Updated about 2 hours ago

Preview of the next-generation LocalStack for Snowflake — a local Snowflake emulator.

Buildkit cache
Image
0

10K+

localstack/snowflake-next repository overview

LocalStack

⁠Overview

localstack/snowflake-next is a preview of the next generation of LocalStack for Snowflake — a Snowflake-compatible emulator that runs on your machine or in CI, so software and data teams can develop and test data pipelines without spending Snowflake Cloud credits.

It is a ground-up reimplementation of localstack/snowflake⁠ with the same goal: existing Snowflake clients connect unmodified. The image speaks the Snowflake driver protocol, so the Python connector, JDBC, .NET, Node.js, Go, SnowSQL, the Snowflake CLI, dbt, and SQLAlchemy all point at it by changing the host only.

☑️ Feature coverage⁠ — what the emulator supports today.

Supported today:

The feature coverage catalog⁠ lists every function (with signatures), SQL command, query feature and data type as supported, partial or not supported — every one Snowflake's SQL reference documents, so the gaps are visible alongside the coverage. It is generated from the emulator itself for every :latest build, and is also available as JSON⁠ for tooling and agents.

Being a preview, coverage is still expanding and behavior may change between releases — the LocalStack for Snowflake docs⁠ describe the current generally-available emulator, localstack/snowflake⁠, which remains the image to use for established workflows. Most of it applies here too; where the two differ, this preview is the one still moving.

⁠Installation

The image is public — no registry login needed:

docker pull localstack/snowflake-next:latest

NOTE: the emulator requires a valid LocalStack auth token whose license carries the Snowflake preview entitlement, passed as the LOCALSTACK_AUTH_TOKEN environment variable. It checks the license at startup and exits without serving if the token is missing or not entitled. Find your token at app.localstack.cloud⁠, see the auth token guide⁠ for details, and talk to us⁠ to get access to the preview.

⁠Start the emulator — docker
docker run \
  --rm -it \
  -p 127.0.0.1:4566:4566 \
  -p 127.0.0.1:443:443 \
  -e LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:?} \
  localstack/snowflake-next

Publish both ports. The container serves TLS and plain HTTP on each: 4566 is LocalStack's usual port, and 443 is what lets clients connect with the connector defaults and the browser reach the worksheet at https://snowflake.localhost.localstack.cloud/.

To serve different ports, set GATEWAY_LISTEN — a comma-separated list, one listener per entry — and publish them the same way:

-e GATEWAY_LISTEN=0.0.0.0:5000 -p 127.0.0.1:5000:5000

Mapping a host port onto a different container port needs nothing else: result downloads and stage transfers follow the address each client connected on.

-p 127.0.0.1:8080:4566
⁠Start the emulator — docker-compose

Create a docker-compose.yml file with the specified content:

services:
  snowflake:
    container_name: "snowflake-next"
    image: localstack/snowflake-next
    ports:
      - "127.0.0.1:4566:4566"
      - "127.0.0.1:443:443"
    environment:
      - LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:?}
      - PERSISTENCE=1
    volumes:
      - "./volume:/var/lib/localstack"

Then start it with:

docker compose up

The container bundles everything it needs and initializes its data directory on first boot. State is ephemeral by default — like every other LocalStack emulator, a restart comes up clean. Set PERSISTENCE=1 and mount /var/lib/localstack — as in the compose file above, or with a named volume (-v snowflake-next-data:/var/lib/localstack) — to keep your databases across restarts. Mounting the volume without PERSISTENCE=1 does not persist anything.

Everything the emulator writes into a bind-mounted volume belongs to the user that owns the mounted directory — the container adopts that uid instead of writing as its own — so removing the directory afterwards never needs sudo.

⁠Web interface

Once the container is up, open https://snowflake.localhost.localstack.cloud/⁠ for the built-in web interface: browse databases, schemas, and tables, run queries in a SQL worksheet, and review query history. On a different port, use the URL the startup log prints. GET /_localstack/health reports the running version.

⁠Quickstart

After starting the emulator, use the Snowflake Python connector⁠ to run a query against it:

import snowflake.connector

connection = snowflake.connector.connect(
    user="test",
    password="test",
    account="test",
    # Resolves to 127.0.0.1 and matches the emulator's default TLS certificate
    host="snowflake.localhost.localstack.cloud",
)
cursor = connection.cursor()
cursor.execute("CREATE DATABASE IF NOT EXISTS test")
cursor.execute("USE DATABASE test")
cursor.execute("CREATE TABLE table1(col1 INT)")
cursor.execute("INSERT INTO table1 VALUES (42)")
print(cursor.execute("SELECT col1 FROM table1").fetchall())    # [(42,)]

The Python and .NET connectors both require a non-empty account. account=test reaches account TEST, while *.sf.sql init scripts seed account SNOWFLAKE by default. To have your init scripts seed the account these snippets use, start the emulator with SNOWFLAKE_INIT_ACCOUNT=TEST, or connect with account=snowflake instead.

⁠.NET 10 (Snowflake.Data 4.x)

Use HTTPS, the default and recommended scheme. For an image published on host port 4566, this is Island's known-working connection string:

var connectionString = "account=test;user=test;password=test;host=snowflake.localhost.localstack.cloud;port=4566;scheme=https;insecuremode=true;role=PUBLIC;MaxPoolSize=4;MULTI_STATEMENT_COUNT=0";

As with the Python snippet, init scripts seed this account=test connection only when the emulator runs with SNOWFLAKE_INIT_ACCOUNT=TEST.

role=PUBLIC is included for clarity and compatibility, but is optional. Snowflake.Data may send an omitted role as a blank roleName=; both blank and omitted values are accepted and resolve to PUBLIC in the emulator, matching the omitted-role behavior recorded against real Snowflake by test_blank_role_is_treated_as_unspecified. An explicitly named role must exist: an unknown role is rejected with error 390189, whose message suggests PUBLIC as an alternative.

The default certificate is publicly trusted, so insecuremode=true is needed only when the emulator falls back to an untrusted/self-signed certificate (or when a deployment supplies one). It is retained above because this known-working configuration also covers that fallback. MaxPoolSize=4 and MULTI_STATEMENT_COUNT=0 are client choices, not emulator requirements.

A fresh instance starts with only the SNOWFLAKE database, and a new session has no current database — create one and USE it (or pass database=... to connect() once it exists), as above.

Credentials are not verified, so any values work — but the password must be a non-empty string, since snowflake-connector-python 4.x rejects an empty one client-side before any request is sent.

snowflake.localhost.localstack.cloud resolves to 127.0.0.1 through public DNS and matches the emulator's default certificate, so TLS validates with no extra client flags. It defaults to port 443, served by the -p 443:443 mapping above. If you only publish 4566 — or your Docker installation does not allow mapping privileged host ports — add port=4566, protocol="https" to connect() instead, and open the web interface at https://snowflake.localhost.localstack.cloud:4566/.

The emulator enables HTTPS by default. On first boot it fetches the LocalStack dev certificate and caches it at /var/lib/localstack/cache/server.test.pem (reused for 24 h, and across restarts when the volume is mounted). On an air-gapped or locked-down host where the download is unreachable, it falls back to a self-signed certificate instead of dropping to plain HTTP — that certificate is not publicly trusted, so connect with insecure_mode=True. Plain-HTTP clients need none of this: every bound port serves both schemes, so protocol="http" / scheme=http works as-is. USE_SSL=false turns TLS off entirely, and that plaintext-only listener cannot serve stage PUT/GET.

An ordinary Docker host-port remap needs no LOCALSTACK_HOST: result-chunk and stage URLs inherit the port from the request's Host. LOCALSTACK_HOST only extends the browser UI's CORS allow-list. If a reverse proxy rewrites Host, set SNOWFLAKE_API_ENDPOINT to the client-visible API base URL and, when stage transfers use a separately advertised address, set SF_S3_ENDPOINT_EXTERNAL to its client-visible host:port.

Check out the documentation⁠ for more examples and guides.

⁠Configuration

VariableDescription
LOCALSTACK_AUTH_TOKENLocalStack auth token (required)
PERSISTENCESet to 1 to keep state across restarts in the mounted /var/lib/localstack volume; ephemeral otherwise. LOCALSTACK_PERSISTENCE (the spelling lstk --persist forwards) is an alias
GATEWAY_LISTENAddress(es) the server binds, comma-separated — one listener per entry (image default 0.0.0.0:4566,0.0.0.0:443)
LOCALSTACK_HOSTRead only for the browser UI's CORS allow-list, as in localstack/snowflake. Not needed for port mappings: every URL the emulator hands back follows the address the client connected on
EXTRA_CORS_ALLOWED_ORIGINSExtra origins appended to the browser UI's CORS allow-list, comma-separated. An entry ending in :// (e.g. file://) matches any origin with that scheme
SNOWFLAKE_INIT_SCRIPTS_DIRDirectory scanned for *.sf.sql init scripts run at startup (default /etc/localstack/init/ready.d)
SNOWFLAKE_INIT_ACCOUNTAccount the init scripts run against, matched like the connector's account (case-insensitive; - and _ equivalent) and created at startup if absent. Default SNOWFLAKE; set TEST to seed the account the quickstart snippets use
CUSTOM_SSL_CERT_PATHPath to a combined PEM (certificate chain + private key) to serve instead of the default certificate. Read-only: served verbatim and never overwritten; an unreadable or malformed file fails startup
SKIP_SSL_CERT_DOWNLOADSet to 1 to skip the certificate download entirely (no network access). With no cached or custom cert the server generates a self-signed one — connect with insecure_mode=True
USE_SSLTLS is on by default and plain HTTP is still served on the same ports. Only false makes the listener plaintext-only, which cannot serve stage PUT/GET
SNOWFLAKE_API_ENDPOINTEndpoint the emulator advertises to clients, e.g. https://sf.example.com:4566 — needed behind a custom domain
SF_S3_ENDPOINT_EXTERNALhost:port the emulator advertises to clients for stage uploads and downloads
SF_S3_ENDPOINTS3 endpoint the emulator itself calls to read external s3:// stages (COPY INTO, LIST, Snowpipe auto-ingest) and managed Iceberg tables. Default http://localhost:4566; point it at a separate LocalStack container with e.g. http://localstack:4566. Never advertised to clients — see SF_S3_ENDPOINT_EXTERNAL
AWS_ENDPOINT_URLFallback for SF_S3_ENDPOINT, used only when that is unset
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKENCredentials for S3-backed stages that carry none of their own. Unset, the AWS default chain is used when AWS_PROFILE is set, otherwise test/test against a LocalStack endpoint
AWS_REGION / AWS_DEFAULT_REGIONSigning region for S3 stage access (AWS_REGION wins); unset, the bucket's region is detected or us-east-1 is used
DNS_NAME_PATTERNS_TO_RESOLVE_UPSTREAMDomain patterns sent to real cloud instead of SF_S3_ENDPOINT, e.g. *.s3.amazonaws.com to load stage data from a real S3 bucket
SF_AZURE_BLOB_ENDPOINTAzure Blob endpoint template for azure:// stages, {account} substituted (default http://{account}.blob.core.azure.localhost.localstack.cloud:4566)
AZURE_STORAGE_SAS_TOKEN / AZURE_STORAGE_ACCOUNT_KEYCredentials for Azure-backed stages that carry none of their own (SAS token preferred)
SF_HOSTNAMESComma-separated hostnames that route to the emulator. Setting it replaces the built-in list rather than extending it, and a request whose Host matches no entry is rejected — include localhost if your clients use it. The first entry also supplies the advertised API host when SNOWFLAKE_API_ENDPOINT is unset
SF_DEFAULT_USERLogin name used when the client sends none (default test); also the user init scripts run as. Passwords are not checked
SF_LOGLog level: trace, debug, info, warn, error, off (a level, not a log-file path)
SF_LOG_SQL_MAX_CHARSCap on the statement text in each executing query log line (default 4000; 0 logs it in full)
DEBUGSet to 1 for debug logging; SF_LOG wins when both are set
RUST_LOGFull tracing env-filter directive, e.g. snowflake_server=debug,tower_http=trace; wins over SF_LOG and DEBUG
DISABLE_EVENTSSet to 1 to disable telemetry
DISABLE_EVENTS_QUERY_DETAILSSet to 1 to keep SQL text out of telemetry while leaving the rest on
OUTBOUND_HTTP_PROXY / OUTBOUND_HTTPS_PROXYProxy for the emulator's outbound license and telemetry calls
SNOWFLAKE_ORGANIZATION_NAMEOrganization name the emulator reports (default LOCALSTACK)
SNOWFLAKE_TASK_SCHEDULER_POLL_SECSHow often tasks, dynamic tables and Snowpipe are polled, in seconds (default 2)
SNOWFLAKE_TIME_TRAVEL_RETENTION_SECSTime-travel window for AT / BEFORE queries, in seconds (default 3600, max 14400; 0 turns it off)
SNOWFLAKE_UNDROP_RETENTION_SECSHow long dropped objects stay restorable with UNDROP, in seconds (default 3600; 0 purges immediately)
SNOWFLAKE_MAX_POOL_SIZEPostgreSQL connections per account, which caps concurrent statements (default vCPUs × 4, clamped to 16–64)
SNOWFLAKE_PYTHON_UDF_INSTALL_DISABLEDSet to 1 to stop Python UDFs installing their PACKAGES on demand (e.g. offline)

⁠Tags

TagContents
latestNewest build — tracks the main branch
X.Y.ZImmutable release build, e.g. 0.1.0 — pin it for a reproducible environment

Both linux/amd64 and linux/arm64 are published.

⁠Security

⚠️ The emulator does not authenticate clients. Any user name and password is accepted, key-pair JWTs are not verified, and object privileges are not enforced. Anyone who can reach its port can read, modify, or drop every database in it — do not expose it beyond a trusted network, and do not load production data into it.

Keep the port on loopback (-p 127.0.0.1:4566:4566, as in the examples above); a plain -p 4566:4566 publishes on every interface. To share one instance, put access control in front of it — an SSH tunnel (ssh -N -L 4566:127.0.0.1:4566 user@remote-host), a private network, or a firewall allowlist — rather than opening the port.

⁠Support

To get in touch with LocalStack to report issues and request new features, reach out on the following channels:

⁠License

© 2026 LocalStack - All Rights Reserved

The LocalStack for Snowflake image is proprietary software and is subject to the LocalStack terms and conditions⁠. Unauthorized use, reproduction, or distribution is prohibited.

Tag summary

Content type

Image

Digest

sha256:22f13198a…

Size

286.3 MB

Last updated

about 2 hours ago

docker pull localstack/snowflake-next