Home Database Snowpark Container Services: Deploy Jobs, Services & Apps on Snowflake
Advanced 5 min · July 17, 2026
Snowpark Container Services: Deploying Jobs, Services, and Apps

Snowpark Container Services: Deploy Jobs, Services & Apps on Snowflake

Learn to deploy jobs, services, and apps using Snowpark Container Services.

N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Written from production experience, not tutorials.

Follow
Production
production tested
2026-07-17T18:30:00
last updated
2,439
articles · all by Naren
Before you start⏱ 15-20 min read
  • Snowflake account with Snowpark Container Services enabled (contact Snowflake support).
  • Docker installed locally.
  • SnowSQL or Python with Snowpark library.
  • Basic knowledge of Docker and YAML.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Snowpark Container Services (SPCS) lets you run containerized workloads (jobs, services, apps) directly on Snowflake.
  • Jobs are short-lived, batch-oriented tasks; services are long-running endpoints; apps are interactive UIs.
  • You need a Snowflake account with SPCS enabled, Docker, and SnowSQL or Python.
  • Deploy via SQL commands like CREATE SERVICE, CREATE JOB, and use Snowpark APIs for orchestration.
  • Key benefits: no data movement, native Snowflake security, and auto-scaling.
✦ Definition~90s read
What is Snowpark Container Services?

Snowpark Container Services is a fully managed container orchestration platform within Snowflake that allows you to deploy, run, and scale Docker containers for jobs, services, and applications.

Think of Snowflake as a giant warehouse.
Plain-English First

Think of Snowflake as a giant warehouse. Normally, you can only move boxes (data) in and out. Snowpark Container Services is like adding a kitchen inside the warehouse: you can cook (run code) right where the ingredients are, without carrying them elsewhere. Jobs are like baking a cake (one-time task), services are like a coffee machine that keeps running, and apps are like a self-serve kiosk.

Imagine you’ve built a machine learning model that predicts customer churn. Traditionally, you’d export data from Snowflake, train the model elsewhere, and then import predictions back. This data shuffle is slow, expensive, and insecure. Snowpark Container Services (SPCS) changes the game: you can run containerized applications—Python, R, Java, or any language—directly inside Snowflake, with no data movement. SPCS supports three workload types: Jobs for batch processing (e.g., daily ETL), Services for long-running endpoints (e.g., REST APIs), and Apps for interactive UIs (e.g., Streamlit dashboards). In this tutorial, you’ll learn how to build, deploy, and debug each type using real-world examples. By the end, you’ll be able to run custom code on Snowflake’s compute, leveraging its security and scalability. Let’s dive in.

Understanding Snowpark Container Services Architecture

Snowpark Container Services (SPCS) is a fully managed container orchestration platform integrated into Snowflake. It allows you to run Docker containers on Snowflake’s compute infrastructure, with native access to Snowflake data and security. The architecture consists of:

  • Compute Pool: A set of Snowflake nodes (like a warehouse) where containers run. You define the instance type (e.g., CPU, memory) and scaling parameters.
  • Image Repository: A Snowflake-managed registry (or external) where you push your Docker images. You can use Snowflake’s built-in registry or connect to Docker Hub or Amazon ECR.
  • Service: A long-running container that exposes an endpoint (e.g., REST API). It can scale horizontally and supports health checks.
  • Job: A short-lived container that runs to completion. Ideal for batch processing, ETL, or training models.
  • App: An interactive web application (e.g., Streamlit) that users can access via a URL.

All containers run in a secure, isolated environment with network policies and role-based access control (RBAC). Data access is handled via Snowflake’s standard permissions—no need to manage credentials inside the container.

Key Components
  • Specification: A YAML or JSON file that defines the container image, resources, environment variables, and endpoints.
  • Service Status: Use SYSTEM$GET_SERVICE_STATUS to monitor health.
  • Logs: Retrieve logs via SYSTEM$GET_SERVICE_LOGS or SYSTEM$GET_JOB_LOGS.

Let’s start by setting up your environment.

setup.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
-- Create a compute pool (if not exists)
CREATE COMPUTE POOL my_pool
  MIN_NODES = 1
  MAX_NODES = 5
  INSTANCE_FAMILY = CPU_X64_XS;

-- Create an image repository (if not exists)
CREATE IMAGE REPOSITORY my_repo;

-- Grant usage to role
GRANT USAGE ON COMPUTE POOL my_pool TO ROLE my_role;
GRANT USAGE ON IMAGE REPOSITORY my_repo TO ROLE my_role;
Output
Compute pool MY_POOL successfully created.
Image repository MY_REPO successfully created.
🔥Compute Pool vs Warehouse
📊 Production Insight
Choose instance family based on workload: CPU for general tasks, GPU for ML training. Start with MIN_NODES=1 to save costs.
🎯 Key Takeaway
SPCS provides a container runtime inside Snowflake, with compute pools, image registries, and SQL-based management.

Building and Pushing a Docker Image to Snowflake

Before deploying, you need a Docker image. Let’s create a simple Python service that returns the current time. We’ll use Snowflake’s image registry.

Step 1: Create a Dockerfile

``dockerfile FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY app.py . CMD ["python", "app.py"] ``

Step 2: Create app.py

```python from flask import Flask, jsonify import datetime

app = Flask(__name__)

@app.route('/time') def get_time(): return jsonify({'time': datetime.datetime.now().isoformat()})

if __name__ == '__main__': app.run(host='0.0.0.0', port=8080) ```

Step 3: Create requirements.txt

`` flask==2.3.2 ``

Step 4: Build and tag the image

``bash docker build -t my_repo/my_service:latest . ``

Step 5: Push to Snowflake

First, authenticate Docker to Snowflake’s registry. Get the registry URL from Snowflake:

``sql SHOW IMAGE REPOSITORIES; ``

Then, log in using SnowSQL or your Snowflake account credentials:

``bash docker login <registry_url> -u <username> --password-stdin ``

``bash docker push <registry_url>/my_repo/my_service:latest ``

Now your image is stored in Snowflake and ready to deploy.

push_image.shBASH
1
2
3
4
5
6
7
8
9
10
11
# Build image
docker build -t my_service:latest .

# Tag for Snowflake registry (replace with your registry URL)
docker tag my_service:latest <registry_url>/my_repo/my_service:latest

# Login (use Snowflake username/password or key pair)
docker login <registry_url> -u <username> --password-stdin

# Push
docker push <registry_url>/my_repo/my_service:latest
Output
The push refers to repository [<registry_url>/my_repo/my_service]
latest: digest: sha256:abc123... size: 1234
💡Use External Registries
📊 Production Insight
Always tag images with a version (not just latest) to enable rollbacks. Use multi-stage builds to reduce image size.
🎯 Key Takeaway
Build a Docker image, tag it with your Snowflake registry URL, and push it using docker push.

Deploying a Long-Running Service

Now let’s deploy the time service as a long-running service. You’ll need a service specification file (YAML).

Step 1: Create service specification

``yaml spec: containers: - name: main image: /my_repo/my_service:latest env: SERVER_PORT: 8080 readinessProbe: port: 8080 path: /time endpoints: - name: time-endpoint port: 8080 public: true ``

Step 2: Create the service

``sql CREATE SERVICE my_time_service IN COMPUTE POOL my_pool FROM SPECIFICATION $$ spec: containers: - name: main image: /my_repo/my_service:latest env: SERVER_PORT: 8080 readinessProbe: port: 8080 path: /time endpoints: - name: time-endpoint port: 8080 public: true $$ MIN_INSTANCES = 1 MAX_INSTANCES = 3; ``

Step 3: Check service status

``sql CALL SYSTEM$GET_SERVICE_STATUS('my_time_service'); ``

Step 4: Get the endpoint URL

``sql SHOW ENDPOINTS IN SERVICE my_time_service; ``

``bash curl https://<ingress_url>/time ``

You should see a JSON response with the current time.

Step 5: Update the service

``sql ALTER SERVICE my_time_service FROM SPECIFICATION $$ ... updated spec ... $$; ``

Snowflake performs a rolling update.

create_service.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- Create service
CREATE SERVICE my_time_service
  IN COMPUTE POOL my_pool
  FROM SPECIFICATION $$
  spec:
    containers:
      - name: main
        image: /my_repo/my_service:latest
        env:
          SERVER_PORT: 8080
        readinessProbe:
          port: 8080
          path: /time
    endpoints:
      - name: time-endpoint
        port: 8080
        public: true
  $$
  MIN_INSTANCES = 1
  MAX_INSTANCES = 3;

-- Check status
CALL SYSTEM$GET_SERVICE_STATUS('my_time_service');

-- Show endpoints
SHOW ENDPOINTS IN SERVICE my_time_service;
Output
Service MY_TIME_SERVICE successfully created.
Status: ACTIVE
Endpoint: https://xyz123.snowflakecomputing.com/time-endpoint
⚠ Public Endpoints
📊 Production Insight
Always define a readiness probe to ensure traffic is only routed to healthy instances. Use private endpoints for internal services.
🎯 Key Takeaway
Services are long-running containers with endpoints. Use CREATE SERVICE with a specification and scale with MIN/MAX_INSTANCES.

Running Batch Jobs with Snowpark Container Services

Jobs are ideal for batch processing, such as daily data transformations or model retraining. Unlike services, jobs run to completion and then stop.

Example: A job that reads data from a Snowflake table and writes results

Create a Python script job.py that uses Snowpark to query data:

```python from snowflake.snowpark import Session import sys

# Get arguments from environment input_table = sys.argv[1] output_table = sys.argv[2]

session = Session.builder.configs({ "connection_name": "snowflake_default" }).create()

df = session.table(input_table) result = df.group_by("category").count() result.write.mode("overwrite").save_as_table(output_table)

session.close() ```

Dockerfile for job:

``dockerfile FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY job.py . ENTRYPOINT ["python", "job.py"] ``

requirements.txt:

`` snowflake-snowpark-python==1.11.0 ``

Build and push the image as before.

Create and execute the job:

``sql EXECUTE JOB my_etl_job IN COMPUTE POOL my_pool FROM SPECIFICATION $$ spec: containers: - name: main image: /my_repo/my_job:latest args: ["MY_DB.MY_SCHEMA.SOURCE_TABLE", "MY_DB.MY_SCHEMA.RESULT_TABLE"] $$; ``

Monitor the job:

``sql SELECT * FROM TABLE(INFORMATION_SCHEMA.JOBS) WHERE NAME='MY_ETL_JOB'; CALL SYSTEM$GET_JOB_LOGS('MY_ETL_JOB'); ``

Jobs automatically terminate after completion. You can also schedule jobs using Snowflake tasks.

create_job.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- Execute a job
EXECUTE JOB my_etl_job
  IN COMPUTE POOL my_pool
  FROM SPECIFICATION $$
  spec:
    containers:
      - name: main
        image: /my_repo/my_job:latest
        args: ["MY_DB.MY_SCHEMA.SOURCE_TABLE", "MY_DB.MY_SCHEMA.RESULT_TABLE"]
  $$;

-- Check job status
SELECT * FROM TABLE(INFORMATION_SCHEMA.JOBS) WHERE NAME='MY_ETL_JOB';

-- Get logs
CALL SYSTEM$GET_JOB_LOGS('MY_ETL_JOB');
Output
Job MY_ETL_JOB executed successfully.
Status: SUCCEEDED
Logs: ...
💡Passing Arguments
📊 Production Insight
For scheduled jobs, wrap the EXECUTE JOB in a Snowflake task with a schedule. Use a compute pool with auto-suspend to save costs.
🎯 Key Takeaway
Jobs are one-time container executions. Use EXECUTE JOB and monitor with INFORMATION_SCHEMA.JOBS.

Building Interactive Apps with Streamlit

Snowpark Container Services also supports interactive web applications, such as Streamlit dashboards. Let’s deploy a simple app that queries Snowflake data.

Step 1: Create a Streamlit app

```python import streamlit as st from snowflake.snowpark import Session

st.title("Snowflake Data Explorer")

session = Session.builder.configs({"connection_name": "snowflake_default"}).create()

table_name = st.text_input("Enter table name", "MY_DB.MY_SCHEMA.MY_TABLE")

if st.button("Query"): df = session.sql(f"SELECT * FROM {table_name} LIMIT 10").collect() st.dataframe(df)

session.close() ```

Step 2: Dockerfile

``dockerfile FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY app.py . EXPOSE 8501 CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.enableCORS=false"] ``

requirements.txt:

`` streamlit==1.28.0 snowflake-snowpark-python==1.11.0 ``

Step 3: Build, push, and create the app

``sql CREATE APPLICATION my_app IN COMPUTE POOL my_pool FROM SPECIFICATION $$ spec: containers: - name: main image: /my_repo/my_app:latest endpoints: - name: app-endpoint port: 8501 public: true $$; ``

Step 4: Access the app

Get the endpoint URL from SHOW ENDPOINTS and open it in a browser.

Note: Apps are essentially services with a web UI. They can be shared with other users via Snowflake’s RBAC.

create_app.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- Create application
CREATE APPLICATION my_app
  IN COMPUTE POOL my_pool
  FROM SPECIFICATION $$
  spec:
    containers:
      - name: main
        image: /my_repo/my_app:latest
    endpoints:
      - name: app-endpoint
        port: 8501
        public: true
  $$;

-- Show endpoint
SHOW ENDPOINTS IN APPLICATION my_app;
Output
Application MY_APP successfully created.
Endpoint: https://xyz123.snowflakecomputing.com/app-endpoint
🔥App vs Service
📊 Production Insight
For production apps, implement authentication (e.g., Snowflake OAuth) and use private endpoints with a load balancer.
🎯 Key Takeaway
Use CREATE APPLICATION to deploy interactive web apps. They are services with a UI, accessible via a URL.

Monitoring and Debugging Containers

Monitoring is crucial for production. Snowflake provides built-in functions to inspect containers.

Service Status

``sql CALL SYSTEM$GET_SERVICE_STATUS('my_service'); `` Returns JSON with instance states (ACTIVE, FAILED, etc.).

Service Logs

``sql CALL SYSTEM$GET_SERVICE_LOGS('my_service', 0, 'main'); `` Where 0 is the instance index and 'main' is the container name.

Job Logs

``sql CALL SYSTEM$GET_JOB_LOGS('my_job'); ``

Metrics

``sql SELECT * FROM TABLE(INFORMATION_SCHEMA.JOBS) WHERE START_TIME > DATEADD('hour', -24, CURRENT_TIMESTAMP); ``

Alerts

Set up Snowflake alerts on job failures or service downtimes using CREATE ALERT.

Common Issues - Container crash: Check logs for exceptions. Ensure all dependencies are installed. - Out of memory: Increase memory in the compute pool or optimize code. - Image pull failure: Verify image path and credentials. - Endpoint not reachable: Check that the port matches the container’s listening port and that the readiness probe passes.

monitor.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
-- Get service status
CALL SYSTEM$GET_SERVICE_STATUS('my_time_service');

-- Get logs for instance 0, container main
CALL SYSTEM$GET_SERVICE_LOGS('my_time_service', 0, 'main');

-- Get job logs
CALL SYSTEM$GET_JOB_LOGS('my_etl_job');

-- Query job history
SELECT * FROM TABLE(INFORMATION_SCHEMA.JOBS)
WHERE NAME = 'MY_ETL_JOB';
Output
{"status":"ACTIVE","instances":[{"instance":0,"state":"ACTIVE","containerName":"main"}]}
Logs: 2024-01-01 12:00:00 INFO: Service started
...
⚠ Log Retention
📊 Production Insight
Set up automated alerts for job failures and service health. Use Snowflake’s event tables to capture custom metrics.
🎯 Key Takeaway
Use SYSTEM$GET_SERVICE_STATUS, SYSTEM$GET_SERVICE_LOGS, and INFORMATION_SCHEMA.JOBS for monitoring.

Security and Best Practices

Security is built-in, but you must follow best practices.

Authentication and Authorization - Use Snowflake’s RBAC to control who can create, manage, and access containers. - Grant USAGE on compute pools and image repositories to specific roles. - For services, use GRANT USAGE ON SERVICE to allow users to call endpoints.

Network Security - By default, containers have no network access to the internet. Use external access integrations to allow outbound connections. - For inbound, use private endpoints (public:false) and connect via Snowflake’s private link.

Secrets Management - Never hardcode credentials. Use Snowflake’s secrets (CREATE SECRET) and reference them in the spec via environment variables.

Resource Management - Set appropriate MIN/MAX_INSTANCES to control costs. - Use auto-suspend for compute pools (not yet available, but you can manually suspend).

Image Security - Scan images for vulnerabilities before pushing. - Use signed images to ensure integrity.

Example: Using a secret

```sql CREATE SECRET my_secret TYPE = PASSWORD USERNAME = 'user' PASSWORD = 'pass';

CREATE SERVICE my_service FROM SPECIFICATION $$ spec: containers: - name: main image: ... env: DB_PASSWORD: { "secret": "my_secret", "key": "password" } $$; ```

security.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- Create a secret
CREATE SECRET db_cred
  TYPE = PASSWORD
  USERNAME = 'admin'
  PASSWORD = 's3cret';

-- Grant usage
GRANT USAGE ON SECRET db_cred TO ROLE my_role;

-- Use in service spec
CREATE SERVICE secure_service
  FROM SPECIFICATION $$
  spec:
    containers:
      - name: main
        image: /my_repo/my_app:latest
        env:
          DB_PASSWORD: { "secret": "db_cred", "key": "password" }
  $$;
Output
Secret DB_CRED successfully created.
Service SECURE_SERVICE successfully created.
⚠ External Access
📊 Production Insight
Regularly rotate secrets and audit access. Use Snowflake’s access history to monitor who is using services.
🎯 Key Takeaway
Use RBAC, secrets, and network policies to secure your containers. Never embed credentials in images.
● Production incidentPOST-MORTEMseverity: high

The Case of the Stuck Service: When a Container Refused to Stop

Symptom
Users reported that the service endpoint returned stale data. Attempts to drop the service timed out.
Assumption
The developer assumed that dropping a service would immediately terminate the container.
Root cause
The service had a long-running connection that wasn't closed gracefully, and Snowflake's default timeout for force-stopping was 10 minutes.
Fix
Used ALTER SERVICE <name> SET MAX_INSTANCES=0 to scale down to zero, then dropped the service. Also added a health check endpoint to allow graceful shutdown.
Key lesson
  • Always implement graceful shutdown in your container (e.g., handle SIGTERM).
  • Use ALTER SERVICE to scale down before dropping if a service is stuck.
  • Set appropriate timeout values for your application.
  • Monitor service status with SYSTEM$GET_SERVICE_STATUS().
  • Test stop/restart behavior in a non-production environment.
Production debug guideSymptom to Action5 entries
Symptom · 01
Service endpoint returns 503
Fix
Check service status with SYSTEM$GET_SERVICE_STATUS(). Ensure the container image is healthy and the port is correct.
Symptom · 02
Job fails with 'Out of memory'
Fix
Increase memory in the job spec or optimize your code. Use SYSTEM$GET_JOB_LOGS to see error details.
Symptom · 03
Cannot drop a service
Fix
First scale down to zero instances: ALTER SERVICE <name> SET MAX_INSTANCES=0. Then drop.
Symptom · 04
Container logs not showing
Fix
Ensure you have MONITOR privilege on the service. Use SYSTEM$GET_SERVICE_LOGS with correct instance and container index.
Symptom · 05
Image pull fails
Fix
Verify the image repository URL and credentials. Check that the image is accessible from Snowflake (public or via external access integration).
★ Quick Debug Cheat SheetCommon symptoms and immediate actions for SPCS
Service not starting
Immediate action
Check image and spec
Commands
CALL SYSTEM$GET_SERVICE_STATUS('my_service');
CALL SYSTEM$GET_SERVICE_LOGS('my_service', 0, 'container1');
Fix now
Correct the image path or port mapping.
Job stuck in 'PENDING'+
Immediate action
Check warehouse size and concurrency
Commands
SELECT * FROM TABLE(INFORMATION_SCHEMA.JOBS) WHERE NAME='my_job';
CALL SYSTEM$GET_JOB_LOGS('my_job');
Fix now
Increase warehouse size or reduce concurrent jobs.
App not loading+
Immediate action
Verify app URL and permissions
Commands
SHOW APPLICATIONS;
CALL SYSTEM$GET_APP_LOGS('my_app');
Fix now
Grant USAGE on the app to users.
FeatureJobServiceApp
DurationShort-lived (runs to completion)Long-running (continuous)Long-running (continuous)
EndpointsNoneREST API (public/private)Web UI (Streamlit, etc.)
ScalingNone (single instance)Horizontal (MIN/MAX_INSTANCES)Horizontal (MIN/MAX_INSTANCES)
Use CaseBatch ETL, model trainingAPI, microserviceDashboard, interactive tool
CreationEXECUTE JOBCREATE SERVICECREATE APPLICATION
⚙ Quick Reference
7 commands from this guide
FileCommand / CodePurpose
setup.sqlCREATE COMPUTE POOL my_poolUnderstanding Snowpark Container Services Architecture
push_image.shdocker build -t my_service:latest .Building and Pushing a Docker Image to Snowflake
create_service.sqlCREATE SERVICE my_time_serviceDeploying a Long-Running Service
create_job.sqlEXECUTE JOB my_etl_jobRunning Batch Jobs with Snowpark Container Services
create_app.sqlCREATE APPLICATION my_appBuilding Interactive Apps with Streamlit
monitor.sqlCALL SYSTEM$GET_SERVICE_STATUS('my_time_service');Monitoring and Debugging Containers
security.sqlCREATE SECRET db_credSecurity and Best Practices

Key takeaways

1
Snowpark Container Services lets you run Docker containers directly on Snowflake, eliminating data movement.
2
Use jobs for batch processing, services for long-running endpoints, and apps for interactive UIs.
3
Monitor containers with SYSTEM$GET_SERVICE_STATUS and logs, and secure them with RBAC and secrets.
4
Always version your images and define readiness probes for services.

Common mistakes to avoid

3 patterns
×

Forgetting to specify a readiness probe

×

Using 'latest' tag in production

×

Not setting MIN_INSTANCES to 0 for cost savings

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the architecture of Snowpark Container Services.
Q02SENIOR
How would you deploy a REST API on Snowflake using SPCS?
Q03SENIOR
What are the security considerations for SPCS?
Q01 of 03SENIOR

Explain the architecture of Snowpark Container Services.

ANSWER
SPCS uses compute pools (nodes) to run containers. Images are stored in a registry. Services and jobs are defined via specifications. Containers have native access to Snowflake data via Snowpark.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between a job and a service in Snowpark Container Services?
02
Can I use GPU instances for container services?
03
How do I pass environment variables to a container?
04
How do I schedule a job to run daily?
05
Can I use a private Docker registry?
N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Written from production experience, not tutorials.

Follow
Verified
production tested
2026-07-17T18:30:00
last updated
2,439
articles · all by Naren
🔥

That's Snowflake. Mark it forged?

5 min read · try the examples if you haven't

Previous
CI/CD: Terraform, dbt, and Schema Evolution
25 / 33 · Snowflake
Next
Native App Framework and Declarative Apps: Building and Distributing