Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.
Modal
Overview
Modal is a serverless platform for running Python code in the cloud with minimal configuration. Execute functions on powerful GPUs, scale automatically to thousands of containers, and pay only for compute used.
Pin versions for reproducibility. Each change invalidates the image cache layer.
Also available: .pip_install_from_pyproject("pyproject.toml") (uses pip, slower than uv).
Getting Local Code into Containers
Three methods, from most to least common:
# Named package — best for multi-file projects (requires __init__.py)
image = image.add_local_python_source("my_package")
# Entire directory — includes non-.py files too
image = image.add_local_dir("src/", remote_path="/root/src")
# Single file — when you only need one script
image = image.add_local_file("train.py", "/root/train.py")
IMPORTANT — add_local_python_source(".") requires a proper Python package (directory with __init__.py). It fails with ModuleNotMountable("no package specified for '.'") for loose scripts. Use a named package: add_local_python_source("my_package").
IMPORTANT — ordering constraint:add_local_file / add_local_python_source MUST come AFTER all build steps (uv_pip_install, run_function, run_commands). Modal mounts these at container startup, not build time. Build steps after local mounts cause: InvalidError('An image tried to run a build step after using image.add_local_*'). Set copy=True to copy into the image layer if you need build steps after.
By default (copy=False), files are added at container startup (fast re-deploys — image doesn't rebuild when code changes). Set copy=True to bake into the image layer (needed if subsequent build steps depend on those files).
Functions
Functions run in the cloud. Import heavy packages inside the function body — they exist in the container but may not be installed locally:
@app.function(image=image, gpu="A10G")deftrain():
import torch # ← inside body, not at top of fileassert torch.cuda.is_available()
Why inside the body? Modal serializes function definitions locally and runs them remotely. Top-level import torch would fail locally if torch isn't installed on your laptop.
Alternative — use the imports() context manager for module-level imports:
with image.imports():
import torch # deferred — only runs in container
Calling Functions
@app.local_entrypoint()defmain():
result = train.remote() # runs on Modalprint(result)
CLI args are auto-parsed from local_entrypoint type hints:
@app.local_entrypoint()defmain(lr: float = 0.001, epochs: int = 10):
train.remote(lr, epochs)
Run: modal run train.py --lr 0.01 --epochs 20
GPUs
@app.function(gpu="A10G") # 24GB, cost-effective@app.function(gpu="L40S") # 48GB, best value for inference@app.function(gpu="A100") # 40/80GB, training@app.function(gpu="H100") # top-tier training@app.function(gpu="H100:4") # multi-GPU@app.function(gpu=["H100", "A100-40GB:2"]) # fallback chain
PyTorch bundles CUDA — debian_slim works fine, no nvidia base image needed.
Volumes: Persistent Storage
Network-attached filesystem that persists across runs. Good for datasets and experiment outputs. Not as fast as local SSD (image layers) — use volumes for data that changes, not for static model weights.
vol = modal.Volume.from_name("my-data", create_if_missing=True)
@app.function(volumes={"/data": vol})defsave_results():
withopen("/data/results.json", "w") as f:
json.dump(results, f)
vol.commit() # persist changes (also auto-commits on exit)
Model Weights: Bake into Image
For static model weights, bake into the image via run_function() — not a volume. Image layers are cached on local SSD; volumes are network reads on every cold start.
HF_SECRET = modal.Secret.from_name("huggingface-secret")
defdownload_model():
from huggingface_hub import snapshot_download
# Use unsloth/ mirrors to avoid HF gated model access issues
snapshot_download("unsloth/Llama-3.2-1B-Instruct", cache_dir="/models")
image = (
modal.Image.debian_slim(python_version="3.14")
.uv_sync()
.uv_pip_install("huggingface_hub[hf_transfer]") # fast Rust-based downloads
.env({"HF_HOME": "/models", "HF_HUB_ENABLE_HF_TRANSFER": "1"})
.run_function(download_model, secrets=[HF_SECRET])
# add_local_* MUST come after all build steps
)
First build downloads the model; subsequent builds reuse the cached image layer.
Secrets
Modal cloud containers do not automatically inherit your local shell environment. Choose the secret source by workflow:
Local-driver jobs where the caller may rotate keys between runs: bridge the current local env with Secret.from_local_environ([...]).
Deployed, scheduled, shared, or reproducible jobs: use a persistent named Modal Secret with required_keys=[...].
# Picks up the current local ANTHROPIC_API_KEY on each `modal run`.
anthropic_secret = modal.Secret.from_local_environ(["ANTHROPIC_API_KEY"])
@app.function(secrets=[anthropic_secret])defuse_anthropic():
import os
token = os.environ["ANTHROPIC_API_KEY"]
modal secret create --force -e main huggingface-secret HF_TOKEN="hf_xxx"
Name persistent Secrets after the service/provider; put the SDK environment variable inside them. required_keys=[...] makes missing or misspelled keys fail clearly when Modal resolves the Secret.
images.md — Base images, packages, local code mounting, caching
secrets.md — Environment variables, auth patterns
scheduled-jobs.md — Cron, periodic tasks
For volumes, scaling, and the latest API changes, use context7 (mcp__context7__query-docs with library ID /llmstxt/modal_llms-full_txt) or https://modal.com/docs/guide.