
LLM mit Modal deployen: Von pip install zum Production-Endpoint
Die meisten Anleitungen zum Selbst-Hosten von LLMs übergehen den schwierigsten Teil: die Infrastruktur. Man kämpft mit CUDA-Treibern, verwaltet Docker-Images, konfiguriert Autoscaling und zahlt trotzdem für idle GPUs um 3 Uhr nachts. Modal eliminiert das alles. Du schreibst Python, deployest und bekommst eine URL.
Diese Anleitung zeigt dir, wie du ein Open-Source-LLM auf Modal mit vLLM als Inferenz-Engine deployest. Am Ende hast du einen live OpenAI-kompatiblen API-Endpunkt auf H100-GPUs, der auf null skaliert, wenn niemand ihn nutzt.
Was ist Modal (und warum für LLMs nutzen)?
Modal ist eine serverlose Computing-Plattform, die speziell für KI-Workloads gebaut wurde. Stell es dir vor wie AWS Lambda, aber mit GPU-Unterstützung, sekundengenauer Abrechnung und einer Python-nativen Entwicklererfahrung. Kein YAML, keine Dockerfiles, kein Kubernetes – du definierst deine gesamte Infrastruktur in einem Python-Skript und deployest mit einem einzigen Befehl.
Deshalb ist es die erste Wahl für LLM-Deployment:
- Scale-to-Zero-Abrechnung – du zahlst nichts, wenn dein Endpunkt keine Anfragen bearbeitet
- Sekundengenaue GPU-Preise – H100s für ~3,70 €/Std., A100 80GB für ~2,35 €/Std., sekundengenau abgerechnet
- Subsekundenkaltstarts – Container starten schnell, besonders mit Memory-Snapshots
- 30 $/Monat Gratis-Credits – genug zum Experimentieren ohne Kreditkartenbelastung
- Kein DevOps – keine Docker-Builds, kein Terraform, kein Cluster-Management
Wenn du LLMs lokal ausführst und ihnen ohne Server-Management eine richtige API geben möchtest, ist Modal der kürzeste Weg dorthin.
Modal vs. RunPod vs. Lambda
| Merkmal | Modal | RunPod | Lambda |
|---|---|---|---|
| Abrechnungsmodell | Sekundengenau, Scale-to-Zero | Sekundengenau, Mindestgebühr | Stündlich, immer aktiv |
| Kaltstart | 2-4 Sekunden | 6-12 Sekunden (groß) | Entfällt (persistent) |
| GPU-Verfügbarkeit | H100, A100, L40S, T4 | A100, H100, A6000 | H100, A100 |
| Infrastruktur | Reines Python, keine Config-Dateien | Docker-basiert, mehr Kontrolle | Vollständiger VM-Zugriff |
| Gratis-Tier | 30 $/Monat Credits | Keins | Keins |
| Am besten für | Bursthafte/Dev-Workloads | Gleichmäßiger Inferenz-Traffic | Hochauslastungs-Training |
Fazit: Modal gewinnt bei burstartigen Workloads und der Entwicklung. Wenn deine GPU-Auslastung dauerhaft über 40 % liegt, ist eine dedizierte Instanz auf RunPod oder Lambda günstiger. Für alles andere – Prototyping, sporadische APIs, Demos – spart Modals Scale-to-Zero-Modell echtes Geld.
Voraussetzungen
Bevor du anfängst, brauchst du drei Dinge:
- Python 3.10+ lokal installiert
- Ein Modal-Konto – kostenlos anmelden unter modal.com
- Ein Hugging Face-Konto – für Modellzugriff (die meisten Modelle sind gesperrt)
Das war's. Keine GPU auf deinem lokalen Rechner, kein CUDA-Toolkit, kein Docker.
Schritt 1: Modal installieren und authentifizieren
Öffne ein Terminal und installiere das Modal Python-Paket:
pip install modalFühre dann den Setup-Befehl aus, um deine lokale Umgebung mit deinem Modal-Konto zu verknüpfen:
modal setupDies öffnet ein Browserfenster zur Authentifizierung. Nach der Bestätigung speichert Modal lokal ein Token. Das musst du nie wieder tun.
Schritt 2: Das Container-Image definieren
Modal-Container werden in Python definiert. Du gibst das Basis-Image an, installierst Abhängigkeiten und setzt Umgebungsvariablen – alles als Code. Erstelle eine Datei namens app.py:
import modal
# Container-Image mit CUDA, Python und vLLM definieren
vllm_image = (
modal.Image.from_registry(
"nvidia/cuda:12.8.0-devel-ubuntu22.04", add_python="3.12"
)
.entrypoint([])
.pip_install(
"vllm==0.13.0",
"huggingface-hub==0.36.0",
)
)
app = modal.App("llm-endpoint", image=vllm_image)Ein paar Dinge fallen auf: Es gibt kein Dockerfile – die modal.Image-Kette ersetzt es vollständig. Das Basis-Image enthält NVIDIA CUDA 12.8 mit Ubuntu 22.04, und wir installieren vLLM und den Hugging Face Hub-Client darüber.
Schritt 3: Modell-Speicher mit Volumes konfigurieren
LLM-Gewichte sind groß (ein 7B-Parameter-Modell ist ~14 GB in fp16). Du willst sie nicht bei jedem Container-Start neu herunterladen. Modal Volumes bieten persistenten Speicher, der direkt in deine Container gemountet wird:
# Persistente Volumes für das Caching von Modellgewichten
hf_cache = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
vllm_cache = modal.Volume.from_name("vllm-cache", create_if_missing=True)
MODEL_NAME = "Qwen/Qwen3-4B-Thinking-2507-FP8"
MODEL_REVISION = "953532f942706930ec4bb870569932ef63038fdf"Wir verwenden hier Qwen3-4B-Thinking (FP8) – ein quantisiertes 4-Milliarden-Parameter-Modell, das schnell, leistungsfähig ist und auf eine einzelne GPU passt. Du kannst es gegen jedes Hugging Face-Modell austauschen: Llama 3.1 8B, Mistral 7B oder alles, was vLLM unterstützt.
Warum FP8? Es reduziert den Speicherbedarf im Vergleich zu fp16 ungefähr um die Hälfte, sodass du größere Modelle auf derselben GPU ausführen oder kleinere Modelle auf günstigeren GPUs betreiben kannst. Wenn dich Quantisierungs-Kompromisse interessieren, behandelt unser Leitfaden zum lokalen Ausführen von LLMs die Präzisionsformate ausführlich.
Schritt 4: Die vLLM-Server-Funktion erstellen
Hier geschieht Modals Magie. Du dekorierst eine Python-Funktion mit GPU-Anforderungen, Skalierungskonfiguration und einer Webserver-Annotation. Modal kümmert sich um den Rest:
N_GPU = 1
MINUTES = 60
VLLM_PORT = 8000
@app.function(
gpu=f"H100:{N_GPU}",
scaledown_window=15 * MINUTES,
timeout=10 * MINUTES,
volumes={
"/root/.cache/huggingface": hf_cache,
"/root/.cache/vllm": vllm_cache,
},
)
@modal.concurrent(max_inputs=32)
@modal.web_server(port=VLLM_PORT, startup_timeout=10 * MINUTES)
def serve():
import subprocess
cmd = [
"vllm", "serve",
MODEL_NAME,
"--revision", MODEL_REVISION,
"--served-model-name", MODEL_NAME,
"--host", "0.0.0.0",
"--port", str(VLLM_PORT),
"--tensor-parallel-size", str(N_GPU),
"--enforce-eager", # Schnellere Kaltstarts
]
subprocess.Popen(" ".join(cmd), shell=True)Lass uns die wichtigsten Dekoratoren aufschlüsseln:
gpu="H100:1"– fordert eine einzelne H100-GPU an. Ändere zu"A100-80GB:1"für günstigere Inferenz oder"H100:2"für 70B+-Modellescaledown_window=15 * MINUTES– hält den Container 15 Minuten nach der letzten Anfrage warm, dann skaliert er auf null@modal.concurrent(max_inputs=32)– erlaubt bis zu 32 gleichzeitige Anfragen pro Container (vLLM handhabt Batching intern)@modal.web_server(port=8000)– exponiert den vLLM-HTTP-Server direkt als Modal-Web-Endpunkt--enforce-eager– überspringt die CUDA-Graph-Kompilierung für schnellere Kaltstarts (Kompromiss: etwas geringerer Spitzendurchsatz)
Das scaledown_window ist dein wichtigster Kostenhebel. Setze es auf 5 Minuten für Dev, 15-30 Minuten für Produktions-APIs mit regelmäßigem Traffic.
Schritt 5: In Produktion deployen
Ein Befehl. Das ist alles:
modal deploy app.pyModal erstellt das Container-Image, pusht es in sein Registry und gibt eine Live-URL zurück:
✓ Created objects.
├── 🔨 Created mount /app.py
├── 🔨 Created volume huggingface-cache
├── 🔨 Created volume vllm-cache
└── 🔨 Created web function serve => https://your-workspace--llm-endpoint-serve.modal.runDas erste Deployment dauert ein paar Minuten, da Modellgewichte in das Volume heruntergeladen werden. Nachfolgende Deployments (und Kaltstarts) sind viel schneller, da die Gewichte gecacht sind.
Für die Entwicklung verwende stattdessen modal serve app.py – es lädt bei Dateiänderungen heiß neu und gibt dir eine temporäre URL.
Schritt 6: Deinen Endpunkt aufrufen (OpenAI-kompatibel)
Dein deployeter vLLM-Server exponiert eine OpenAI-kompatible API unter /v1/chat/completions. Du kannst das Standard-OpenAI Python SDK verwenden, um ihn aufzurufen – zeige einfach die Basis-URL auf deinen Modal-Endpunkt:
from openai import OpenAI
client = OpenAI(
api_key="not-needed", # vLLM erfordert standardmäßig keine Authentifizierung
base_url="https://your-workspace--llm-endpoint-serve.modal.run/v1",
)
response = client.chat.completions.create(
model="Qwen/Qwen3-4B-Thinking-2507-FP8",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain what vLLM is in two sentences."},
],
temperature=0.7,
max_tokens=256,
)
print(response.choices[0].message.content)Das funktioniert auch mit curl:
curl -X POST https://your-workspace--llm-endpoint-serve.modal.run/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-4B-Thinking-2507-FP8",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 128
}'Jedes Tool, das eine OpenAI-kompatible API unterstützt, funktioniert – LangChain, LlamaIndex, deine eigene App. Wenn du Anfragen über mehrere LLM-Endpunkte routest, kann ein LLM-Gateway-Tool dir bei Failover und Load-Balancing helfen.
Tipps zur Kostenoptimierung
Modals sekundengenaue Abrechnung ist bereits effizienter als stündliche Preisgestaltung, aber du kannst noch mehr herausholen:
1. FP8-Quantisierung verwenden
FP8-Modelle verbrauchen ungefähr halb so viel VRAM wie ihre fp16-Entsprechungen. Ein Qwen3-8B in FP8 passt auf eine einzelne H100, während die fp16-Version den größten Teil der 80 GB dieser GPU benötigt. Weniger VRAM bedeutet, dass du günstigere GPUs (A100 40GB, L40S) für kleinere Modelle verwenden kannst.
2. Das Scaledown-Fenster anpassen
Der scaledown_window-Parameter steuert, wie lange ein Container nach der letzten Anfrage warm bleibt:
| Szenario | Empfohlenes Fenster | Warum |
|---|---|---|
| Entwicklung/Tests | 5 Minuten | Geld sparen, Kaltstarts sind in Ordnung |
| Interne API (gelegentlich) | 10-15 Minuten | Balance zwischen Kosten und Latenz |
| Produktion (regelmäßiger Traffic) | 20-30 Minuten | Kaltstarts minimieren |
| Hochfrequenz-Produktion | min_containers=1 verwenden | Einen immer warm halten |
3. Die richtige GPU wählen
Nicht immer H100 nehmen. Kleinere Modelle brauchen das nicht:
| Modellgröße | Empfohlene GPU | Ungefähre Kosten/Std. |
|---|---|---|
| 1-4B Parameter | L4 oder T4 | 0,55 – 0,75 € |
| 7-8B Parameter | A10 oder L40S | 1,05 – 1,85 € |
| 13-14B Parameter | A100 40GB | 2,00 € |
| 30-70B Parameter | A100 80GB oder H100 | 2,35 – 3,70 € |
| 70B+ Parameter | H100 x2 | 7,40 € |
4. Prompt-Caching aktivieren
Wenn deine Workloads wiederholte System-Prompts oder gemeinsame Präfixe beinhalten, kann vLLMs automatisches Präfix-Caching Latenz und Rechenaufwand erheblich reduzieren. Aktiviere es, indem du --enable-prefix-caching zum vLLM-Serve-Befehl hinzufügst. Für einen tieferen Einblick, wie Caching bei verschiedenen Anbietern funktioniert, sieh dir unseren LLM Prompt-Caching-Leitfaden an.
5. --enforce-eager für Kaltstart-Optimierung verwenden
Standardmäßig kompiliert vLLM CUDA-Graphen beim Start, was 1-3 zusätzliche Minuten dauert. Das --enforce-eager-Flag überspringt diese Kompilierung. Du tauschst ~10-15 % Spitzendurchsatz gegen dramatisch schnellere Kaltstarts ein. Für bursthafte Workloads, bei denen Latenz wichtiger als roher Durchsatz ist, ist es fast immer die richtige Wahl.
Weiterführend: Fine-Tuned Modelle
Sobald du mit dem Deployment von Basismodellen vertraut bist, ist der natürliche nächste Schritt das Deployment deiner eigenen feinabgestimmten Version. Der Workflow ist identisch – du zeigst MODEL_NAME einfach auf dein Hugging Face-Repository oder ein Modal-Volume mit deinen feinabgestimmten Gewichten.
Modal unterstützt auch das Ausführen von Fine-Tuning-Jobs direkt auf ihren GPUs. Du kannst einen LoRA-Adapter auf Modal trainieren, ihn in einem Volume speichern und das zusammengeführte Modell deployen – alles ohne die Plattform zu verlassen. Unser LLM Fine-Tuning-Leitfaden behandelt die Trainingsseite ausführlich.
Die vollständige app.py
Hier ist das vollständige Deployment-Skript als kopierfertigem Block:
import modal
# --- Image-Definition ---
vllm_image = (
modal.Image.from_registry(
"nvidia/cuda:12.8.0-devel-ubuntu22.04", add_python="3.12"
)
.entrypoint([])
.pip_install("vllm==0.13.0", "huggingface-hub==0.36.0")
)
# --- Volumes für Modell-Caching ---
hf_cache = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
vllm_cache = modal.Volume.from_name("vllm-cache", create_if_missing=True)
# --- Modell-Konfiguration ---
MODEL_NAME = "Qwen/Qwen3-4B-Thinking-2507-FP8"
MODEL_REVISION = "953532f942706930ec4bb870569932ef63038fdf"
app = modal.App("llm-endpoint", image=vllm_image)
N_GPU = 1
MINUTES = 60
VLLM_PORT = 8000
@app.function(
gpu=f"H100:{N_GPU}",
scaledown_window=15 * MINUTES,
timeout=10 * MINUTES,
volumes={
"/root/.cache/huggingface": hf_cache,
"/root/.cache/vllm": vllm_cache,
},
)
@modal.concurrent(max_inputs=32)
@modal.web_server(port=VLLM_PORT, startup_timeout=10 * MINUTES)
def serve():
import subprocess
cmd = [
"vllm", "serve",
MODEL_NAME,
"--revision", MODEL_REVISION,
"--served-model-name", MODEL_NAME,
"--host", "0.0.0.0",
"--port", str(VLLM_PORT),
"--tensor-parallel-size", str(N_GPU),
"--enforce-eager",
]
subprocess.Popen(" ".join(cmd), shell=True)Mit modal deploy app.py deployen, MODEL_NAME gegen ein beliebiges Hugging Face-Modell austauschen, und du bist live.
Häufig gestellte Fragen
Wie viel kostet es, ein LLM auf Modal zu betreiben?
Das hängt von der GPU und davon ab, wie lange dein Endpunkt warm bleibt. Ein Qwen3-4B auf einem H100 kostet ~3,70 €/Std. aktiver Nutzung. Mit Scale-to-Zero und einem 15-minütigen Scaledown-Fenster könnte ein wenig genutzter Endpunkt 5-15 €/Monat kosten. Die 30 $ Gratis-Credits pro Monat decken viele Experimente ab.
Skaliert Modal auf null?
Ja – das ist einer der wichtigsten Vorteile. Wenn für die Dauer deines scaledown_window keine Anfragen eintreffen, fährt der Container herunter und du hörst auf zu zahlen. Die nächste Anfrage löst einen Kaltstart aus (typischerweise 2-10 Sekunden, je nach Modellgröße und ob du --enforce-eager verwendest).
Kann ich Llama 3.1 oder Mistral auf Modal deployen?
Absolut. Tausche die MODEL_NAME-Konstante gegen ein beliebiges von vLLM unterstütztes Modell aus: meta-llama/Llama-3.1-8B-Instruct, mistralai/Mistral-7B-Instruct-v0.3 oder Hunderte andere auf Hugging Face. Für 70B+-Modelle ändere N_GPU auf 2 und verwende gpu="H100:2".
Wie vergleichen sich Kaltstarts mit RunPod?
Modal-Kaltstarts dauern typischerweise 2-4 Sekunden für den Container selbst, plus Modell-Ladezeit. Mit in einem Volume gecachten Modellgewichten und aktiviertem --enforce-eager sind es insgesamt 10-30 Sekunden für ein 7-8B-Modell. RunPods serverlose Kaltstarts reichen von unter 200 ms (gecacht) bis 6-12 Sekunden für größere Container – obwohl ihr Always-On-Modell Kaltstarts vollständig vermeidet.
Ist Modals vLLM-Endpunkt wirklich OpenAI-kompatibel?
Ja. vLLM implementiert dieselben /v1/chat/completions-, /v1/completions- und /v1/models-Endpunkte, die OpenAI verwendet. Du kannst das offizielle openai Python SDK auf deine Modal-URL zeigen und es funktioniert sofort. Streaming, Function Calling und JSON-Modus funktionieren alle.
Brauche ich eine GPU auf meinem lokalen Rechner?
Nein. Dein lokaler Rechner führt nur die Modal CLI aus. Die gesamte GPU-Arbeit findet auf Modals Cloud-Infrastruktur statt. Du könntest von einem Chromebook aus deployen, wenn du wolltest.
Wie füge ich meinem Endpunkt Authentifizierung hinzu?
Modal-Web-Endpunkte sind standardmäßig öffentlich. Für die Produktion füge eine einfache API-Key-Prüfung in deinen Anwendungscode ein oder nutze Modals integrierte Web-Authentifizierungsfunktionen. Du kannst auch eine Proxy-Schicht mit einem LLM-Gateway einrichten, das Auth, Rate Limiting und Routing handhabt.
Was ist der Unterschied zwischen modal serve und modal deploy?
modal serve erstellt einen temporären Endpunkt, der bei Code-Änderungen heiß neu lädt – perfekt für die Entwicklung. modal deploy erstellt einen persistenten, produktionsreifen Endpunkt mit einer stabilen URL. Verwende serve beim Iterieren, deploy wenn du bereit zum Ausliefern bist.
Kann ich SGLang anstelle von vLLM verwenden?
Ja. Modals Dokumentation enthält SGLang-Beispiele neben vLLM. SGLang hat tendenziell weniger Overhead für decode-lastige Workloads und kleinere Modelle. vLLM ist generell besser für gemischte Workloads mit schwerem Prefill geeignet. Beide produzieren OpenAI-kompatible Endpunkte.
Wie verhält sich das im Vergleich zum Deployment auf Railway oder Render?
Plattformen wie Railway, Render und Fly.io sind großartig für Web-Apps, bieten aber keine GPU-Instanzen. Modal ist speziell für GPU-Workloads mit sekundengenauer Abrechnung und Autoscaling gebaut. Wenn du ein LLM bereitstellen musst, ist Modal (oder RunPod) das richtige Werkzeug – traditionelle PaaS-Plattformen können das nicht.