Code: github.com/sfc-gh-kkeller/spcs-devcontainer — Dockerfile, SQL setup scripts, and service spec. MIT licensed.
The Idea#
Snowflake Container Services (SPCS) is typically used for deploying production workloads. But it is also a fully managed container runtime with compute pools, persistent storage, network isolation, and auto-suspend. That makes it a surprisingly good platform for development environments.
This project turns SPCS into a personal cloud IDE: VS Code in the browser, a web terminal, persistent home directory, and direct access to your Snowflake data — all running inside Snowflake’s infrastructure. No local setup. Open a browser, start coding.
Think of it as IaaS powered by Snowflake.
What You Get#
| Component | Purpose |
|---|---|
| OpenVSCode Server | Full VS Code IDE in your browser (port 3000) |
| ttyd | Web-based terminal access (port 7681) |
| supervisord (Go) | Process manager — auto-restarts crashed services |
| pixi | Fast package manager for Python, Node, Rust, and anything from conda-forge |
| asdf | Multi-language version manager (600+ plugins) |
| GitHub CLI | gh for pull requests, issues, and repo management |
| Persistent /home/pixi | Backed by a Snowflake stage — survives restarts |
Pre-installed: curl, wget, git, vim (with NERDTree, airline, fugitive), jq, htop, ssh, and standard Unix tools.
Architecture#
┌──────────────────────────────────────────────────────────┐
│ SPCS Compute Pool │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Dev Container (Debian) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ OpenVSCode │ :3000 │ ttyd │ :7681 │ │
│ │ │ Server │ │ Terminal │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ │ │
│ │ └──────────┬──────────┘ │ │
│ │ │ │ │
│ │ ┌────────▼────────┐ │ │
│ │ │ supervisord │ │ │
│ │ └─────────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────────▼─────────────────────────┐ │ │
│ │ │ /home/pixi (Snowflake Stage) │ │ │
│ │ │ code, tools, configs — all persisted │ │ │
│ │ └───────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────▼────────────────┐ │
│ │ External Access Integration │ │
│ │ GitHub, PyPI, npm, conda-forge │ │
│ └─────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘Two public endpoints are exposed: one for VS Code, one for the terminal. Both authenticate through Snowflake’s built-in endpoint authentication — only users with the right role can access them.
Setup#
1. Build and Push the Image#
git clone https://github.com/sfc-gh-kkeller/spcs-devcontainer.git
cd devcontainer-spcs
# Build for SPCS (linux/amd64 required)
docker build --platform linux/amd64 -t devcontainer:latest .
# Login to Snowflake registry and push
snow spcs image-registry login
docker tag devcontainer:latest $REPO_URL/devcontainer:latest
docker push $REPO_URL/devcontainer:latest2. Create Snowflake Infrastructure#
-- Database, schema, image repo
CREATE DATABASE IF NOT EXISTS DEVCONTAINER_DB;
CREATE SCHEMA IF NOT EXISTS DEVCONTAINER_DB.SPCS;
CREATE IMAGE REPOSITORY IF NOT EXISTS DEVCONTAINER_DB.SPCS.IMAGES;
-- Persistent storage for home directory
CREATE STAGE IF NOT EXISTS DEVCONTAINER_DB.SPCS.HOME_STAGE
DIRECTORY = (ENABLE = TRUE)
ENCRYPTION = (TYPE = 'SNOWFLAKE_SSE');
-- Compute pool
CREATE COMPUTE POOL IF NOT EXISTS DEVCONTAINER_POOL
MIN_NODES = 1 MAX_NODES = 1
INSTANCE_FAMILY = CPU_X64_S
AUTO_SUSPEND_SECS = 3600
AUTO_RESUME = TRUE;
-- Network access for package managers
CREATE NETWORK RULE IF NOT EXISTS DEVCONTAINER_DB.SPCS.EXTERNAL_ACCESS_RULE
MODE = EGRESS TYPE = HOST_PORT
VALUE_LIST = ('0.0.0.0:80', '0.0.0.0:443');
CREATE EXTERNAL ACCESS INTEGRATION IF NOT EXISTS DEVCONTAINER_EXTERNAL_ACCESS
ALLOWED_NETWORK_RULES = (DEVCONTAINER_DB.SPCS.EXTERNAL_ACCESS_RULE)
ENABLED = TRUE;3. Deploy the Service#
CREATE SERVICE DEVCONTAINER_DB.SPCS.DEVCONTAINER_SERVICE
IN COMPUTE POOL DEVCONTAINER_POOL
FROM SPECIFICATION $$
spec:
containers:
- name: devcontainer
image: /devcontainer_db/spcs/images/devcontainer:latest
env:
HOME: "/home/pixi"
USER: "pixi"
volumeMounts:
- name: pixi-home
mountPath: /home/pixi
resources:
requests:
memory: 2Gi
cpu: 1000m
limits:
memory: 4Gi
cpu: 2000m
readinessProbe:
port: 3000
path: /
endpoints:
- name: vscode
port: 3000
public: true
- name: terminal
port: 7681
public: true
volumes:
- name: pixi-home
source: "@devcontainer_db.spcs.home_stage"
uid: 1000
gid: 1000
$$
EXTERNAL_ACCESS_INTEGRATIONS = (DEVCONTAINER_EXTERNAL_ACCESS)
MIN_INSTANCES = 1
MAX_INSTANCES = 1;4. Get Your URLs#
SHOW ENDPOINTS IN SERVICE DEVCONTAINER_DB.SPCS.DEVCONTAINER_SERVICE;Two URLs: one for VS Code, one for the terminal. Open either in your browser and start working.
Working Inside the Container#
Install Languages#
With pixi (fast, from conda-forge):
pixi global install python
pixi global install nodejs
pixi global install rust
pixi global install ripgrep fd-find batWith asdf (600+ plugins):
asdf plugin add python && asdf install python 3.12.0 && asdf global python 3.12.0
asdf plugin add nodejs && asdf install nodejs 22.0.0 && asdf global nodejs 22.0.0
asdf plugin add golang && asdf install golang 1.22.0 && asdf global golang 1.22.0Connect to Snowflake From Inside#
pixi global install snowflake-cli
snow connection add
snow sql -q "SELECT * FROM my_table LIMIT 10"You are already inside Snowflake’s network. No VPN, no firewall rules, no connection strings pointing to the internet.
What Persists and What Doesn’t#
The /home/pixi directory is backed by a Snowflake stage:
| Persists | Does Not Persist |
|---|---|
| Installed tools (pixi, asdf) | System packages (apt install) |
| Your code in ~/workspace | /tmp files |
| Shell history, dotfiles, configs | Running processes |
| VS Code extensions and settings | Environment variables |
To persist system packages, add them to the Dockerfile and rebuild. Your data on the stage is safe — updating the service image preserves the volume.
Cost Management#
The compute pool auto-suspends after 1 hour of inactivity. Adjust for your workflow:
-- Shorter auto-suspend (15 minutes)
ALTER COMPUTE POOL DEVCONTAINER_POOL SET AUTO_SUSPEND_SECS = 900;
-- Manually suspend when done for the day
ALTER COMPUTE POOL DEVCONTAINER_POOL SUSPEND;Pay only for compute time. The stage storage is minimal (pennies). When the pool is suspended, cost is zero.
Customisation#
More power:
ALTER COMPUTE POOL DEVCONTAINER_POOL SET INSTANCE_FAMILY = CPU_X64_M;Tighter network:
-- Only allow specific hosts
CREATE OR REPLACE NETWORK RULE DEVCONTAINER_DB.SPCS.RESTRICTED_ACCESS
MODE = EGRESS TYPE = HOST_PORT
VALUE_LIST = ('github.com:443', 'pypi.org:443', 'registry.npmjs.org:443');More tools in the image:
RUN apt-get update && apt-get install -y your-packageRebuild, push, update the service — your persistent data is untouched.
Key Takeaways#
SPCS is not just for production. A compute pool with auto-suspend and persistent storage is a fully capable development platform.
Browser-only access. VS Code and terminal in the browser. No local Docker, no SSH keys, no port forwarding. Open the URL and code.
Everything persists. Tools, code, configs, extensions — all on a Snowflake stage. Kill the service, restart it a week later, pick up where you left off.
Direct Snowflake access. You are inside the Snowflake network. Query data, manage objects, build and test UDFs — all from the same environment.
Cost-effective. Auto-suspend means you pay for compute only when actively using it. Stage storage costs are negligible.
See also: Data Sovereignty: Querying On-Premise Iceberg from Snowflake for another SPCS-based architecture, and Nanocortex for building AI agents that run in these environments.
