Preview of the next-generation LocalStack for Snowflake — a local Snowflake emulator.
10K+
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:
VARIANT, OBJECT, ARRAY,
and structured typesPUT/GET, and COPY INTO
— internal stages plus external stages on Amazon S3UNDROP, dynamic tables,
materialized views, streams, and tasksGEOGRAPHY, GEOMETRY)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.
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.
dockerdocker 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
docker-composeCreate 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.
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.
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,)]
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";
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.
| Variable | Description |
|---|---|
LOCALSTACK_AUTH_TOKEN | LocalStack auth token (required) |
PERSISTENCE | Set to 1 to keep state across restarts in the mounted /var/lib/localstack volume; ephemeral otherwise |
GATEWAY_LISTEN | Address(es) the server binds, comma-separated — one listener per entry (image default 0.0.0.0:4566,0.0.0.0:443) |
LOCALSTACK_HOST | Read 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 |
SNOWFLAKE_INIT_SCRIPTS_DIR | Directory scanned for *.sf.sql init scripts run at startup (default /etc/localstack/init/ready.d) |
CUSTOM_SSL_CERT_PATH | Path 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_DOWNLOAD | Set 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 |
SNOWFLAKE_API_ENDPOINT | Endpoint the emulator advertises to clients, e.g. https://sf.example.com:4566 — needed behind a custom domain |
SF_S3_ENDPOINT_EXTERNAL | host:port the emulator advertises to clients for stage uploads and downloads |
SF_HOSTNAMES | Comma-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_LOG | Log level: trace, debug, info, warn, error, off (a level, not a log-file path) |
DEBUG | Set to 1 for debug logging; SF_LOG wins when both are set |
RUST_LOG | Full tracing env-filter directive, e.g. snowflake_server=debug,tower_http=trace; wins over SF_LOG and DEBUG |
DISABLE_EVENTS | Set to 1 to disable telemetry |
| Tag | Contents |
|---|---|
latest | Newest build — tracks the main branch |
X.Y.Z | Immutable release build, e.g. 0.1.0 — pin it for a reproducible environment |
Both linux/amd64 and linux/arm64 are published.
⚠️ 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.
To get in touch with LocalStack to report issues and request new features, reach out on the following channels:
© 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.
Content type
Image
Digest
sha256:d24f3070d…
Size
260.7 MB
Last updated
about 5 hours ago
docker pull localstack/snowflake-next