Modal
Overview
Modal is a cloud platform for running Python code serverlessly, with a focus on AI/ML workloads. Key capabilities:
- GPU compute on demand (T4, L4, A10, L40S, A100, H100, H200, B200)
- Serverless functions with autoscaling from zero to thousands of containers
- Custom container images built entirely in Python code
- Persistent storage via Volumes for model weights and datasets
- Web endpoints for serving models and APIs
- Scheduled jobs via cron or fixed intervals
- Sub-second cold starts for low-latency inference
Everything in Modal is defined as code — no YAML, no Dockerfiles required (though both are supported).
When to Use This Skill
Use this skill when:
- Deploy or serve AI/ML models in the cloud
- Run GPU-accelerated computations (training, inference, fine-tuning)
- Create serverless web APIs or endpoints
- Scale batch processing jobs in parallel
- Schedule recurring tasks (data pipelines, retraining, scraping)
- Need persistent cloud storage for model weights or datasets
- Want to run code in custom container environments
- Build job queues or async task processing systems
Installation and Authentication
Install
uv pip install modal
The Modal Python SDK supports Python 3.10–3.14. This skill targets the stable modal>=1.0 API (current release: 1.4.x).
Authenticate
Prefer existing credentials before creating new ones. Only the two Modal-specific
variables below are relevant — do not read, load, or expose any other environment
variables or .env file contents:
- Check whether
MODAL_TOKEN_IDandMODAL_TOKEN_SECRETare already set in the current environment. - If not, look up only those two keys in a local
.envfile (ignore all other entries) and load them if appropriate for the workflow. - Only fall back to interactive
modal setupor generating fresh tokens if neither source already provides those two values.
modal setup
This opens a browser for authentication. For CI/CD or headless environments, use environment variables:
export MODAL_TOKEN_ID=<your-token-id>
export MODAL_TOKEN_SECRET=<your-token-secret>
If tokens are not already available in the environment or .env, generate them at https://modal.com/settings
Modal offers a free tier with $30/month in credits.
Reference: See references/getting-started.md for detailed setup and first app walkthrough.
Core Concepts
App and Functions
A Modal App groups related functions. Functions decorated with @app.function() run remotely in the cloud:
import modal
app = modal.App("my-app")
@app.function()
def square(x):
return x ** 2
@app.local_entrypoint()
def main():
# .remote() runs in the cloud
print(square.remote(42))
Run with modal run script.py. Deploy with modal deploy script.py.
Reference: See references/functions.md for lifecycle hooks, classes, .map(), .spawn(), and more.
Container Images
Modal builds container images from Python code. The recommended package installer is uv:
image = (
modal.Image.debian_slim(python_version="3.11")
.uv_pip_install("torch==2.12.0", "transformers==5.9.0", "accelerate==1.13.0")
.apt_install("git")
)
@app.function(image=image)
def inference(prompt):
from transformers import pipeline
pipe = pipeline("text-generation", model="meta-llama/Llama-3-8B")
return pipe(prompt)
Key image methods:
.uv_pip_install()— Install Python packages with uv (recommended).pip_install()— Install with pip (fallback).apt_install()— Install system packages.run_commands()— Run shell commands during build.run_function()— Run Python during build (e.g., download model weights).add_local_python_source()— Add local modules.env()— Set environment variables
Reference: See references/images.md for Dockerfiles, micromamba, caching, GPU build steps.
GPU Compute
Request GPUs via the gpu parameter:
@app.function(gpu="H100")
def train_model():
import torch
device = torch.device("cuda")
# GPU training code here
# Multiple GPUs
@app.function(gpu="H100:4")
def distributed_training():
...
# GPU fallback chain
@app.function(gpu=["H100", "A100-80GB", "A100-40GB"])
def flexible_inference():
...
Available GPUs: T4, L4, A10, L40S, A100-40GB, A100-80GB, RTX-PRO-6000, H100, H200, B200, B200+
- GPUs are always specified as strings (e.g.
gpu="H100",gpu="H100:4"). The oldmodal.gpu.*objects are deprecated as of v0.73.31. - Up to 8 GPUs per container (except A10: up to 4)
- L40S is recommended for inference (cost/performance balance, 48 GB VRAM)
- H100/A100 can be auto-upgraded to H200/A100-80GB at no extra cost
- Use
gpu="H100!"to prevent auto-upgrade
Reference: See references/gpu.md for GPU selection guidance and multi-GPU training.
Volumes (Persistent Storage)
Volumes provide distributed, persistent file storage:
vol = modal.Volume.from_name("model-weights", create_if_missing=True)
@app.function(volumes={"/data": vol})
def save_model():
# Write to the mounted path
with open("/data/model.pt", "wb") as f:
torch.save(model.state_dict(), f)
@app.function(volumes={"/data": vol})
def load_model():
model.load_state_dict(torch.load("/data/model.pt"))
- Optimized for write-once, read-many workloads (model weights, datasets)
- CLI access:
modal volume ls,modal volume put,modal volume get - Background auto-commits every few seconds
- Mount read-only or limit to a subdirectory with
vol.with_mount_options(read_only=True, sub_path="subset")
Reference: See references/volumes.md for v2 volumes, concurrent writes, and best practices.
Secrets
Securely pass credentials to functions:
@app.function(secrets=[modal.Secret.from_name("my-api-keys")])
def call_api():
import os
api_key = os.environ["API_KEY"]
# Use the key
Create secrets via CLI: modal secret create my-api-keys API_KEY=sk-xxx
Or from a .env file: modal.Secret.from_dotenv()
Reference: See references/secrets.md for dashboard setup, multiple secrets, and templates.
Web Endpoints
Serve models and APIs as web endpoints:
@app.function()
@modal.fastapi_endpoint()
def predict(text: str):
return {"result": model.predict(text)}
modal serve script.py— Development with hot reload and temporary URLmodal deploy script.py— Production deployment with permanent URL- Supports FastAPI, ASGI (Starlette, FastHTML), WSGI (Flask, Django), WebSockets
- Request bodies up to 4 GiB, unlimited response size
Reference: See references/web-endpoints.md for ASGI/WSGI apps, streaming, auth, and WebSockets.
Scheduled Jobs
Run functions on a schedule:
@app.function(schedule=modal.Cron("0 9 * * *")) # Daily at 9 AM UTC
def daily_pipeline():
# ETL, retraining, scraping, etc.
...
@app.function(schedule=modal.Period(hours=6))
def periodic_check():
...
Deploy with modal deploy script.py to activate the schedule.
modal.Cron("...")— Standard cron syntax, stable across deploysmodal.Period(hours=N)— Fixed interval, resets on redeploy- Monitor runs in the Modal dashboard
Reference: See references/scheduled-jobs.md for cron syntax and management.
Scaling and Concurrency
Modal autoscales containers automatically. Configure limits:
@app.function(
max_containers=100, # Upper limit
min_containers=2, # Keep warm for low latency
buffer_containers=5, # Reserve capacity
scaledown_window=300, # Idle seconds before shutdown
)
def process(data):
...
Process inputs in parallel with .map():
results = list(process.map([item1, item2, item3, ...]))
Enable concurrent request handling per container with @modal.concurrent. Set
target_inputs (the autoscaler's per-container target) below max_inputs (the hard