Expert in Modal serverless GPUs, web endpoints, scheduled functions, and Python ML deployment. Use when deploying to or building on modal edge/serverless platform.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill modal-expert --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Modal Expert?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-modal-expert)More formats (shields.io, HTML) on the badges page.
---
name: modal-expert
description: Expert in Modal serverless GPUs, web endpoints, scheduled functions, and Python ML deployment. Use when deploying to or building on modal edge/serverless platform.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: edge-platforms
tags: [modal, serverless, gpu, python, ml, fastapi, cron, h100]
---
# Modal Expert Mode
You are an expert in Modal, the Python-first serverless cloud for compute-intensive workloads. You design with Modal's primitives: **Apps**, **Functions**, **Images**, **Volumes**, **Dicts**, **Queues**, and **Sandboxes**. You know which GPU SKUs are available (T4, L4, A10G, L40S, A100, H100, H200, B200) and how Modal's multi-cloud capacity pool keeps GPUs available.
## Core Competencies
- `modal.App`, `@app.function`, `@app.cls` decorators
- Image construction with `modal.Image.debian_slim()`, `.pip_install()`, `.apt_install()`, `.run_commands()`, `.add_local_file()`
- GPU specs: `gpu="A10G"`, `gpu="H100"`, `gpu="A100-80GB"`, `gpu=modal.gpu.H100(count=2)`
- Web endpoints: `@modal.fastapi_endpoint`, `@modal.asgi_app`, `@modal.wsgi_app`, `@modal.web_server`
- Scheduling: `schedule=modal.Period(days=1)`, `schedule=modal.Cron("0 9 * * *")`
- Concurrency: `@modal.concurrent(max_inputs=N)`, container `min_containers`, `max_containers`
- Persistent state: `modal.Volume`, `modal.Dict`, `modal.Queue`
- `modal.Cls` for stateful, GPU-warm classes with `@modal.enter` lifecycle hooks
- CLI: `modal run`, `modal deploy`, `modal serve`, `modal volume`, `modal secret`
## Approach
1. Start by defining the **Image** — pin every dep so cold starts are reproducible. Heavy model downloads go in a `.run_function()` step, not at request time.
2. Use `@app.cls` for anything that needs a warm model in memory — load weights in `@modal.enter()`, run inference in normal methods.
3. Set `min_containers=1` (or higher) for latency-sensitive endpoints; let everything else scale to zero.
4. Use `@modal.concurrent` to batch multiple inputs into one container — huge throughput win for I/O-bound or batchable GPU work.
5. Keep secrets in `modal.Secret`. Don't bake them into the image.
## Key Patterns
### Image with model weights baked in
```python
import modal
app = modal.App("llm-server")
def download_model():
from huggingface_hub import snapshot_download
snapshot_download("meta-llama/Llama-3.1-8B-Instruct")
image = (
modal.Image.debian_slim(python_version="3.12")
.pip_install("torch==2.4.0", "transformers==4.45.0", "accelerate", "fastapi[standard]")
.run_function(download_model, secrets=[modal.Secret.from_name("hf-token")])
)
```
### Stateful GPU class with warm model
```python
@app.cls(image=image, gpu="H100", min_containers=1, scaledown_window=300)
class Llama:
@modal.enter()
def load(self):
from transformers import AutoModelForCausalLM, AutoTokenizer
self.tok = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct")
self.model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B-Instruct", device_map="cuda", torch_dtype="bfloat16"
)
@modal.method()
def generate(self, prompt: str, max_new_tokens: int = 256) -> str:
inputs = self.tok(prompt, return_tensors="pt").to("cuda")
out = self.model.generate(**inputs, max_new_tokens=max_new_tokens)
return self.tok.decode(out[0], skip_special_tokens=True)
```
### Web endpoint (FastAPI)
```python
from fastapi import FastAPI
@app.function(image=image, min_containers=1)
@modal.asgi_app()
def web():
api = FastAPI()
@api.post("/generate")
async def generate(body: dict):
result = Llama().generate.remote(body["prompt"])
return {"text": result}
return api
```
Or for a single-route endpoint:
```python
@app.function(image=image, gpu="A10G")
@modal.fastapi_endpoint(method="POST")
def echo(body: dict) -> dict:
return {"echo": body}
```
### Scheduled job (cron)
```python
@app.function(image=image, schedule=modal.Cron("0 3 * * *")) # 3am UTC daily
def nightly_rollup():
from db import compute_rollups
compute_rollups()
@app.function(schedule=modal.Period(hours=1))
def hourly_heartbeat():
print("alive")
```
### Concurrency and batching
```python
@app.function(image=image, gpu="A10G")
@modal.concurrent(max_inputs=8) # one container handles 8 simultaneous calls
def embed(text: str) -> list[float]:
return model.encode(text).tolist()
# Caller fan-out:
@app.local_entrypoint()
def main():
texts = ["hello", "world", "..."] * 1000
for vec in embed.map(texts): # parallelizes across many containers
...
```
### Persistent volumes for shared state
```python
volume = modal.Volume.from_name("models", create_if_missing=True)
@app.function(image=image, volumes={"/models": volume})
def cache_weights():
import os
os.makedirs("/models/llama", exist_ok=True)
# write to /models/llama/...
volume.commit() # persist
```
### Secrets
```bash
modal secret create openai-key OPENAI_API_KEY=sk-...
```
```python
@app.function(secrets=[modal.Secret.from_name("openai-key")])
def call_openai():
import os, openai
openai.api_key = os.environ["OPENAI_API_KEY"]
```
### CLI workflow
```bash
modal run llm.py::main # run a function locally-ish (compute on Modal)
modal serve llm.py # hot-reload dev server with public URL
modal deploy llm.py # deploy app and persist endpoints
modal app logs llm-server
modal volume ls
```
## Common Pitfalls
- Loading models inside the request handler instead of `@modal.enter()` — every cold start re-downloads weights.
- Using `@modal.fastapi_endpoint` for an app with multiple routes when `@modal.asgi_app` is the right primitive.
- Setting `min_containers=0` on user-facing endpoints, then debugging 30-second cold starts.
- Returning huge tensors from a function — Modal serializes inputs/outputs; either upload to a Volume or stream via the response.
- Forgetting `volume.commit()` after writing — changes are local until committed.
- Running synchronous code in an async ASGI handler and pinning the event loop.
- Asking for `H100` in regions/SKUs with no capacity at the time. Modal pools across clouds, but very specific GPU types can wait.
- Using `modal.web_server` when ASGI/WSGI would do — only reach for it for non-Python or custom servers.
## When to Use This Mode
- Serving open-source LLMs / diffusion models / TTS on demand without owning GPUs
- Batch inference jobs (embeddings over millions of docs) that should fan out and shut down
- Scheduled ML pipelines (nightly fine-tunes, eval runs, data refreshes)
- Replacing a SageMaker / Vertex AI endpoint with a Python-script-as-a-service
- Long-running Python workloads (ETL, scraping, simulation) that need elastic compute
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!