Skip to content
Back to skills

Modal Expert

ASecurity

Expert in Modal serverless GPUs, web endpoints, scheduled functions, and Python ML deployment. Use when deploying to or building on modal edge/serverless platform.

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
devopspythongobashfastapidebuggingapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill modal-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Modal Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Modal Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-modal-expert/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-modal-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.