modal-gpu-retry
Install
pip install modal-gpu-retry
You also need it inside your Modal image, since Modal imports your module in the container:
image = modal.Image.debian_slim().pip_install("torch", "modal-gpu-retry")
Motivation
When running hundreds of vLLM jobs on Modal, I would often batch deploy these as independent jobs, and frustratingly some jobs would OOM and require me to tediously figure out the exact subset of all jobs that failed and rerun them manually. I looked into what features Modal had to resolve this, but their built-in retry feature, retries, which runs the job again if it fails, is ill-suited for OOM issues since it runs the job on the same hardware config.
I needed fallback functionality that escalated to larger GPUs when a job failed, so I made this lightweight package: modal-gpu-retry.
Usage
All you have to do is change the decorator from
@app.function(gpu="L40S", image=image)
to
@gpuretry.function(app, gpu="L40S", retries=["A100", "H100"], image=image)
If the job fails on the L40S, it will then be run on the A100, then if it fails again, on the H100.
Example
Here's an example implementation.
import modal
import modal_gpu_retry as gpuretry
app = modal.App("my-evals")
image = modal.Image.debian_slim().pip_install("torch", "modal-gpu-retry")
# before: @app.function(gpu="L40S", image=image)
@gpuretry.function(app, gpu="L40S", retries=["A100", "H100"], image=image)
def run_eval(config):
... # if this OOMs on L40S, it runs again on A100, then H100
@app.local_entrypoint()
def main():
results = list(run_eval.map(configs))
The first attempt uses the gpu= you already set. Each failure moves to the next
GPU in the list. Run it the way you normally would, with modal run evals.py. It
works on @gpuretry.cls too, and .remote, .map, and .starmap keep working as they
did.
Modal's retries
Modal already has a retries argument, but it reruns the job with the same
hardware configuration:
@app.function(gpu="L40S", retries=3)
This is not helpful when jobs fail due to OOM, because rerunning the same job on the same GPU just runs out of memory again. This package uses the same argument but accepts a list of GPUs, and each retry uses the next one:
@gpuretry.function(app, gpu="L40S", retries=["A100", "H100"])
So:
retries=3behaves like normal Modal (rerun the same GPU).retries=["A100", "H100"]reruns on a bigger GPU each time.retries=[]is equivalent toretries=0.
If a job fails on all GPUs specified, you get a LadderExhausted back in the results
instead of an exception, so one bad job doesn't kill the batch:
results = list(run_eval.map(configs))
dead = [c for c, r in zip(configs, results, strict=True)
if isinstance(r, gpuretry.LadderExhausted)]
Detached runs
.remote, .map, and .starmap run the retry loop in your process, so they stop
if you disconnect. .spawn_map runs the loop inside a lightweight CPU orchestrator
(dispatched as an independent Modal job) instead, so it keeps going after you close
your laptop:
handle = run_eval.spawn_map(configs)
results = handle.get() # later, or from a different process
To pick it back up elsewhere, pass the call id to gpuretry.LadderCall.from_id(call_id).
Using the Modal CLI
Run your script the normal way — escalation happens inside the local_entrypoint
when you call .remote, .map, or .starmap:
modal run evals.py
modal deploy evals.py works too, and is required before you use .spawn_map.
Limitations
These Modal CLI patterns don't work as intended:
modal run evals.py::run_eval— the CLI can't target the wrapped function directly; call it from alocal_entrypointinstead.modal run --detach evals.py— keeps the app alive, but the retry loop for.remote,.map, and.starmapstill stops when you disconnect; use.spawn_mapfor escalation that survives a disconnect.
See the examples README for the full explanation.
Notes
spawn_mapneeds the app deployed (modal deploy), because the orchestrator looks up the target by name.- Your class or function shows up in the Modal dashboard with a
_mgr_real_prefix. That's how the wrapper keeps your call sites unchanged without breaking the way Modal loads your class inside the container.
License
MIT. This is a community wrapper around the modal SDK and isn't affiliated with Modal.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file modal_gpu_retry-0.0.3.tar.gz.
File metadata
- Download URL: modal_gpu_retry-0.0.3.tar.gz
- Upload date:
- Size: 137.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fccd8213f958820c36654e44e1d1da2caf61934a6a6b2af2dbbd770400f139e
|
|
| MD5 |
f43a08a3c3d555ac03657f750a63a9d2
|
|
| BLAKE2b-256 |
d3f621508834c4d65b79cff76bb332dcead50415a0ccdbea078d109f802c7117
|
Provenance
The following attestation bundles were made for modal_gpu_retry-0.0.3.tar.gz:
Publisher:
publish.yml on alexkranias/modal-gpu-retry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modal_gpu_retry-0.0.3.tar.gz -
Subject digest:
4fccd8213f958820c36654e44e1d1da2caf61934a6a6b2af2dbbd770400f139e - Sigstore transparency entry: 1860866206
- Sigstore integration time:
-
Permalink:
alexkranias/modal-gpu-retry@fc5416d9ab86f1b7205f35a3760164c2be78c884 -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/alexkranias
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc5416d9ab86f1b7205f35a3760164c2be78c884 -
Trigger Event:
release
-
Statement type:
File details
Details for the file modal_gpu_retry-0.0.3-py3-none-any.whl.
File metadata
- Download URL: modal_gpu_retry-0.0.3-py3-none-any.whl
- Upload date:
- Size: 11.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fd3b11724feac6af4b37f62cbcf4e6a60d9b7984af3d4005d03a2455ee16e82
|
|
| MD5 |
7a509eea10acf1ac21883eb27b0d62bc
|
|
| BLAKE2b-256 |
19ca374c514e17df9a003e8f43a2138466403a5f2238a2266dc868632c2b8ddc
|
Provenance
The following attestation bundles were made for modal_gpu_retry-0.0.3-py3-none-any.whl:
Publisher:
publish.yml on alexkranias/modal-gpu-retry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modal_gpu_retry-0.0.3-py3-none-any.whl -
Subject digest:
4fd3b11724feac6af4b37f62cbcf4e6a60d9b7984af3d4005d03a2455ee16e82 - Sigstore transparency entry: 1860866349
- Sigstore integration time:
-
Permalink:
alexkranias/modal-gpu-retry@fc5416d9ab86f1b7205f35a3760164c2be78c884 -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/alexkranias
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc5416d9ab86f1b7205f35a3760164c2be78c884 -
Trigger Event:
release
-
Statement type: