| name | flash |
| description | runpod-flash SDK and CLI for deploying AI workloads on Runpod serverless GPUs/CPUs. |
| user-invocable | true |
Runpod Flash
Write code locally, iterate with flash dev — it runs your functions on remote Runpod GPUs/CPUs with hot-reload and live worker logs — then flash deploy to ship. Endpoint handles provisioning.
Setup
uv tool install runpod-flash
pip install runpod-flash
flash login
flash login --no-open
flash login --timeout 300
export RUNPOD_API_KEY=your_key
flash init my-project
flash init .
flash init my-project --force
flash update
flash update --version 1.16.0
flash init writes AGENTS.md (+ a CLAUDE.md symlink). To add them to an existing project: python -c "from runpod_flash.rules import install_agent_files; from pathlib import Path; install_agent_files(Path.cwd())".
CLI
flash dev is the canonical dev-server command (flash run still works as a hidden alias).
flash dev
flash dev --auto-provision
flash dev --port 9000 --host 0.0.0.0
flash deploy
flash deploy --env staging
flash deploy --app my-app --env prod
flash deploy --preview
flash deploy --no-deps --python-version 3.11
flash env list
flash env create staging
flash env get staging
flash env delete staging
flash app list
flash app create my-app
flash app get my-app
flash app delete my-app
flash undeploy list
flash undeploy my-endpoint
flash undeploy --all
flash undeploy --cleanup-stale
flash build
flash build --no-deps
Dev vs Deploy
flash dev — iterate. Local server at :8888, but your decorated functions
execute on remote GPU/CPU workers. Hot-reloads on save and streams the worker's
logs live to the terminal. No build/upload/deploy wait — use this the whole time you
develop.
flash deploy — ship. Builds an artifact and deploys a stable endpoint. Slow
(build + upload + provision); only do this once the code works under flash dev.
flash dev ships only the function body to the worker, so a NameError for a
module-level name surfaces immediately here. flash deploy imports the whole module and
can mask that bug (see Gotcha #1). Develop against flash dev and you catch it first.
Autonomous Dev Loop
flash dev is a long-running server — run it in the background (don't block on it),
capture its output, and drive it over HTTP. The captured log is the remote worker's live
stream (cold start, model load, prints, tracebacks) — read it to debug.
flash dev > /tmp/flash-dev.log 2>&1 &
until grep -q "flash dev localhost:" /tmp/flash-dev.log; do sleep 2; done
URL=$(grep -o "localhost:[0-9]*" /tmp/flash-dev.log | head -1)
curl -s "$URL/main/predict" -d '{"data": {...}}'
- Read the real URL from the log — flash auto-bumps the port if 8888 is in use, and
prints
✓ flash dev localhost:<port> plus the route table.
- Routes are namespaced by file:
main.py's /predict is served at /main/predict.
- A handler typed
def predict(data: dict) expects the arg as a top-level field — send
{"data": {...}}, not the bare object (otherwise 422).
- Edit a handler and save — hot-reload re-syncs the body; just re-send the request, no
redeploy. Add
--auto-provision to skip the first-call cold start. kill %1 when done.
Endpoint: Three Modes
Mode 1: Your Code (Queue-Based Decorator)
One function = one endpoint with its own workers.
from runpod_flash import Endpoint, GpuGroup
@Endpoint(name="my-worker", gpu=GpuGroup.AMPERE_80, workers=5, dependencies=["torch"])
async def compute(data):
import torch
return {"sum": torch.tensor(data, device="cuda").sum().item()}
result = await compute([1, 2, 3])
Mode 2: Your Code (Load-Balanced Routes)
Multiple HTTP routes share one pool of workers.
from runpod_flash import Endpoint, GpuGroup
api = Endpoint(name="my-api", gpu=GpuGroup.ADA_24, workers=(1, 5), dependencies=["torch"])
@api.post("/predict")
async def predict(data: list[float]):
import torch
return {"result": torch.tensor(data, device="cuda").sum().item()}
@api.get("/health")
async def health():
return {"status": "ok"}
Mode 3: External Image (Client)
Deploy a pre-built Docker image and call it via HTTP.
from runpod_flash import Endpoint, GpuGroup, PodTemplate
server = Endpoint(
name="my-server",
image="my-org/my-image:latest",
gpu=GpuGroup.AMPERE_80,
workers=1,
env={"HF_TOKEN": "xxx"},
template=PodTemplate(containerDiskInGb=100),
)
result = await server.post("/v1/completions", {"prompt": "hello"})
models = await server.get("/v1/models")
job = await server.run({"prompt": "hello"})
await job.wait()
print(job.output)
Connect to an existing endpoint by ID (no provisioning):
ep = Endpoint(id="abc123")
job = await ep.runsync({"prompt": "hello"})
print(job.output)
How Mode Is Determined
| Parameters | Mode |
|---|
name= only | Decorator (your code) |
image= set | Client (deploys image, then HTTP calls) |
id= set | Client (connects to existing, no provisioning) |
Endpoint Constructor
Endpoint(
name="endpoint-name",
id=None,
gpu=GpuGroup.AMPERE_80,
cpu=CpuInstanceType.CPU5C_4_8,
workers=5,
workers=(1, 5),
max_concurrency=1,
idle_timeout=60,
dependencies=["torch"],
system_dependencies=["ffmpeg"],
image="org/image:tag",
env={"KEY": "val"},
volume=NetworkVolume(...),
datacenter=DataCenter.US_CA_2,
gpu_count=1,
template=PodTemplate(containerDiskInGb=100),
flashboot=True,
accelerate_downloads=True,
min_cuda_version=CudaVersion.V12_8,
scaler_type=ServerlessScalerType.QUEUE_DELAY,
scaler_value=4,
execution_timeout_ms=0,
)
gpu= and cpu= are mutually exclusive
gpu= accepts a GpuGroup, a GpuType, or a list of either (see GPU Types below)
workers=5 means (0, 5). Default is (0, 1)
max_concurrency -- requests handled concurrently per worker (default 1). Raise it for I/O-bound LB routes so one worker serves multiple requests
idle_timeout default is 60 seconds
flashboot=True (default) -- enables fast cold starts via snapshot restore
gpu_count -- GPUs per worker (default 1), use >1 for multi-GPU models
datacenter -- a DataCenter enum, list, or string; defaults to None (unset)
scaler_type -- defaults to QUEUE_DELAY for queue-based endpoints and REQUEST_COUNT for load-balanced endpoints; pass ServerlessScalerType.QUEUE_DELAY or REQUEST_COUNT to override
DataCenter, CudaVersion, and ServerlessScalerType are importable from runpod_flash
NetworkVolume
NetworkVolume(name="my-vol", size=100)
PodTemplate
PodTemplate(
containerDiskInGb=64,
dockerArgs="",
ports="",
startScript="",
)
EndpointJob
Returned by ep.run() and ep.runsync() in client mode.
job = await ep.run({"data": [1, 2, 3]})
await job.wait(timeout=120)
print(job.id, job.output, job.error, job.done)
await job.cancel()
GPU Types
gpu= accepts a GpuGroup (a supply pool by VRAM tier), a GpuType (a pinned GPU model), or a list of either. GpuGroup picks the cheapest available GPU within a tier; GpuType pins a specific model.
GpuGroup (supply pool)
| Enum | GPU | VRAM |
|---|
ANY | any | varies |
AMPERE_16 | RTX A4000 / A4500 / RTX 4000 Ada / RTX 2000 Ada | 16GB |
AMPERE_24 | RTX A5000 / L4 / RTX 3090 | 24GB |
AMPERE_48 | A40 / RTX A6000 | 48GB |
AMPERE_80 | A100 (PCIe / SXM4) | 80GB |
ADA_24 | RTX 4090 | 24GB |
ADA_32_PRO | RTX 5090 | 32GB |
ADA_48_PRO | RTX 6000 Ada / L40 / L40S | 48GB |
ADA_80_PRO | H100 PCIe (80GB) / H100 HBM3 (80GB) / H100 NVL (94GB) | 80GB+ |
HOPPER_141 | H200 | 141GB |
BLACKWELL_96 | RTX PRO 6000 Blackwell | 96GB |
BLACKWELL_180 | B200 | 180GB |
GpuType (pinned model)
Pin an exact GPU model. Members include NVIDIA_GEFORCE_RTX_4090, NVIDIA_GEFORCE_RTX_5090, NVIDIA_RTX_6000_ADA_GENERATION, NVIDIA_H100_80GB_HBM3, NVIDIA_A100_80GB_PCIe, NVIDIA_A100_SXM4_80GB, NVIDIA_H200, NVIDIA_B200, the NVIDIA_RTX_PRO_6000_BLACKWELL_* editions (Server / Workstation / Max-Q), and the Ampere/Ada RTX A-series models (NVIDIA_RTX_A4000, A4500, A5000, A6000, NVIDIA_L4, NVIDIA_A40, NVIDIA_GEFORCE_RTX_3090, NVIDIA_RTX_4000_ADA_GENERATION, NVIDIA_RTX_2000_ADA_GENERATION).
from runpod_flash import Endpoint, GpuType
@Endpoint(name="pinned", gpu=GpuType.NVIDIA_GEFORCE_RTX_4090, dependencies=["torch"])
async def report_gpu(data):
import torch
return {"gpu": torch.cuda.get_device_name(0)}
CPU Types (CpuInstanceType)
| Enum | vCPU | RAM | Max Disk | Type |
|---|
CPU3G_1_4 | 1 | 4GB | 10GB | General |
CPU3G_2_8 | 2 | 8GB | 20GB | General |
CPU3G_4_16 | 4 | 16GB | 40GB | General |
CPU3G_8_32 | 8 | 32GB | 80GB | General |
CPU3C_1_2 | 1 | 2GB | 10GB | Compute |
CPU3C_2_4 | 2 | 4GB | 20GB | Compute |
CPU3C_4_8 | 4 | 8GB | 40GB | Compute |
CPU3C_8_16 | 8 | 16GB | 80GB | Compute |
CPU5C_1_2 | 1 | 2GB | 15GB | Compute (5th gen) |
CPU5C_2_4 | 2 | 4GB | 30GB | Compute (5th gen) |
CPU5C_4_8 | 4 | 8GB | 60GB | Compute (5th gen) |
CPU5C_8_16 | 8 | 16GB | 120GB | Compute (5th gen) |
from runpod_flash import Endpoint, CpuInstanceType
@Endpoint(name="cpu-work", cpu=CpuInstanceType.CPU5C_4_8, workers=5, dependencies=["pandas"])
async def process(data):
import pandas as pd
return pd.DataFrame(data).describe().to_dict()
Common Patterns
CPU + GPU Pipeline
from runpod_flash import Endpoint, GpuGroup, CpuInstanceType
@Endpoint(name="preprocess", cpu=CpuInstanceType.CPU5C_4_8, workers=5, dependencies=["pandas"])
async def preprocess(raw):
import pandas as pd
return pd.DataFrame(raw).to_dict("records")
@Endpoint(name="infer", gpu=GpuGroup.AMPERE_80, workers=5, dependencies=["torch"])
async def infer(clean):
import torch
t = torch.tensor([[v for v in r.values()] for r in clean], device="cuda")
return {"predictions": t.mean(dim=1).tolist()}
async def pipeline(data):
return await infer(await preprocess(data))
Parallel Execution
import asyncio
results = await asyncio.gather(compute(a), compute(b), compute(c))
Gotchas
- Only the function body ships to the worker -- most common error. Put imports and any module-level constants/helpers the function uses inside the decorated body.
flash deploy imports the whole module so module globals happen to work; flash dev ships just the body, so a module-level name raises NameError. A handler that works deployed can break under dev — fix it by moving everything inside.
- Forgetting await -- all decorated functions and client methods need
await.
- Missing dependencies -- must list in
dependencies=[].
- gpu/cpu are exclusive -- pick one per Endpoint.
- idle_timeout is seconds -- default 60s, not minutes.
- 10MB payload limit -- pass URLs, not large objects.
- Client vs decorator --
image=/id= = client. Otherwise = decorator.
- Auto GPU switching requires workers >= 5 -- pass a list of GPU types (e.g.
gpu=[GpuGroup.ADA_24, GpuGroup.AMPERE_80]) and set workers=5 or higher. The platform only auto-switches GPU types based on supply when max workers is at least 5.
runsync timeout is 60s -- cold starts can exceed 60s. Use ep.runsync(data, timeout=120) for first requests or use ep.run() + job.wait() instead.
Resources