
Driftsätt en LLM med Modal: från pip install till produktionsendpoint
De flesta guider om att självhosta LLM:er hoppar över den svåraste delen: infrastrukturen. Man kämpar med CUDA-drivrutiner, hanterar Docker-images, konfigurerar autoskalning och betalar ändå för inaktiva GPU:er klockan 3 på natten. Modal eliminerar allt det. Du skriver Python, du driftsätter, du får en URL.
Den här guiden tar dig igenom hur du driftsätter en open source-LLM på Modal med vLLM som inferensmotor. I slutet har du en live OpenAI-kompatibel API-endpoint på H100-GPU:er som skalar till noll när ingen använder den.
Vad är Modal (och varför använda det för LLM:er)?
Modal är en serverlös beräkningsplattform byggd specifikt för AI-arbetsbelastningar. Tänk AWS Lambda, men med GPU-stöd, sekundfakturering och en Python-nativ utvecklarupplevelse. Ingen YAML, inga Dockerfiles, ingen Kubernetes — du definierar hela din infrastruktur i ett Python-skript och driftsätter med ett enda kommando.
Här är varför det blivit standardvalet för LLM-driftsättning:
- Scale-to-zero-fakturering — du betalar ingenting när din endpoint inte hanterar förfrågningar
- GPU-priser per sekund — H100:or till ~$3,95/tim, A100 80 GB till ~$2,50/tim, fakturerat per sekund
- Sub-sekunds kallstarter — containrar startar snabbt, särskilt med minnessnapshots
- $30/månad i gratis krediter — tillräckligt för att experimentera utan kreditkortsavgifter
- Ingen DevOps — inga Docker-byggen, ingen Terraform, ingen klusterhantering
Om du kör LLM:er lokalt och vill ge dem ett ordentligt API utan att hantera servrar, är Modal den kortaste vägen dit.
Modal vs. RunPod vs. Lambda
| Funktion | Modal | RunPod | Lambda |
|---|---|---|---|
| Faktureringsmodell | Per sekund, scale-to-zero | Per sekund, minsta avgift | Per timme, alltid aktiv |
| Kallstart | 2-4 sekunder | 6-12 sekunder (stor) | Ej aktuellt (persistent) |
| GPU-tillgänglighet | H100, A100, L40S, T4 | A100, H100, A6000 | H100, A100 |
| Infrastruktur | Ren Python, inga konfigfiler | Docker-baserad, mer kontroll | Full VM-åtkomst |
| Gratisnivå | $30/månad i krediter | Ingen | Ingen |
| Bäst för | Burstig/dev-arbetsbelastning | Stabil inferenstrafik | Intensiv träning |
Slutsats: Modal vinner för burstig arbetsbelastning och utveckling. Om din GPU-användning konsekvent överstiger 40% är en dedikerad instans på RunPod eller Lambda billigare. För allt annat — prototypning, intermittenta API:er, demos — sparar Modals scale-to-zero-modell riktiga pengar.
Förutsättningar
Innan du börjar behöver du tre saker:
- Python 3.10+ installerat lokalt
- Ett Modal-konto — registrera dig gratis på modal.com
- Ett Hugging Face-konto — för modelltillgång (de flesta modeller är begränsade)
Det är allt. Ingen GPU på din lokala maskin, inget CUDA-verktygspaket, ingen Docker.
Steg 1: Installera Modal och autentisera
Öppna en terminal och installera Modal Python-paketet:
pip install modalKör sedan installationskommandot för att koppla din lokala miljö till ditt Modal-konto:
modal setupDet öppnar ett webbläsarfönster för autentisering. När du bekräftar sparar Modal en token lokalt. Du behöver aldrig göra det igen.
Steg 2: Definiera containerbilden
Modal-containrar definieras i Python. Du anger basbilden, installerar beroenden och ställer in miljövariabler — allt som kod. Skapa en fil som heter app.py:
import modal
# Definiera containerbilden med CUDA, Python och vLLM
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)Några saker att notera. Det finns ingen Dockerfile — den modal.Image-kedjan ersätter den helt. Basbilden inkluderar NVIDIA CUDA 12.8 med Ubuntu 22.04, och vi installerar vLLM och Hugging Face Hub-klienten ovanpå.
Steg 3: Konfigurera modelllagring med Volumes
LLM-vikter är stora (en modell med 7 miljarder parametrar är ~14 GB i fp16). Du vill inte ladda ner dem varje gång en container startar. Modal Volumes ger dig persistent lagring som monteras direkt i dina containrar:
# Persistenta volumes för att cacha modellvikter
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"Vi använder Qwen3-4B-Thinking (FP8) här — en kvantiserad modell med 4 miljarder parametrar som är snabb, kapabel och passar på en enda GPU. Du kan byta ut den mot vilken Hugging Face-modell som helst: Llama 3.1 8B, Mistral 7B, eller vad som helst som vLLM stödjer.
Varför FP8? Det halverar ungefär minnesanvändningen jämfört med fp16, vilket innebär att du kan köra större modeller på samma GPU — eller mindre modeller på billigare GPU:er. Om du är nyfiken på kvantiseringsavvägningarna täcker vår guide om att köra LLM:er lokalt precisionsformaten i detalj.
Steg 4: Skapa vLLM-serverfunktionen
Det är här Modals magi sker. Du dekorerar en Python-funktion med GPU-krav, skalningskonfiguration och en webbserverannotation. Modal sköter allt annat:
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", # Snabbare kallstarter
]
subprocess.Popen(" ".join(cmd), shell=True)Låt oss bryta ner de viktigaste dekoratörerna:
gpu="H100:1"— begär en enda H100-GPU. Ändra till"A100-80GB:1"för billigare inferens, eller"H100:2"för 70B+-modellerscaledown_window=15 * MINUTES— håller containern varm i 15 minuter efter den senaste förfrågan, sedan skalar den till noll@modal.concurrent(max_inputs=32)— tillåter upp till 32 samtidiga förfrågningar per container (vLLM hanterar batching internt)@modal.web_server(port=8000)— exponerar vLLM HTTP-servern direkt som en Modal-webbendpoint--enforce-eager— hoppar över CUDA-grafkompilering för snabbare kallstarter (avvägning: något lägre toppgenomströmning)
scaledown_window är din huvudsakliga kostnadspak. Ställ in den på 5 minuter för dev, 15-30 minuter för produktions-API:er med regelbunden trafik.
Steg 5: Driftsätt till produktion
Ett kommando. Det är allt:
modal deploy app.pyModal bygger containerbilden, laddar upp den till sitt register och returnerar en live-URL:
✓ 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.runDen första driftsättningen tar några minuter eftersom den laddar ner modellvikter till volymen. Efterföljande driftsättningar (och kallstarter) är mycket snabbare eftersom vikterna är cachade.
Använd modal serve app.py under utveckling istället — det laddar om vid filändringar och ger dig en tillfällig URL.
Steg 6: Anropa din endpoint (OpenAI-kompatibel)
Din driftsatta vLLM-server exponerar ett OpenAI-kompatibelt API på /v1/chat/completions. Du kan använda standard OpenAI Python SDK för att anropa det — peka bara bas-URL:en mot din Modal-endpoint:
from openai import OpenAI
client = OpenAI(
api_key="not-needed", # vLLM kräver inte autentisering som standard
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)Det fungerar också med 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
}'Vilket verktyg som helst som stödjer ett OpenAI-kompatibelt API fungerar — LangChain, LlamaIndex, din egna app. Om du dirigerar förfrågningar över flera LLM-endpoints kan ett LLM-gateway-verktyg hjälpa dig att hantera failover och lastbalansering.
Tips för kostnadsoptimering
Modals sekundfakturering är redan mer effektivt än timdebitering, men du kan pressa ut ännu mer:
1. Använd FP8-kvantiering
FP8-modeller använder ungefär hälften av VRAM jämfört med deras fp16-motsvarigheter. En Qwen3-8B i FP8 passar på en enda H100, medan fp16-versionen behöver det mesta av den GPU:ns 80 GB. Mindre VRAM innebär att du kan använda billigare GPU:er (A100 40 GB, L40S) för mindre modeller.
2. Justera nedskaleringsfönstret
Parametern scaledown_window styr hur länge en container förblir varm efter den senaste förfrågan:
| Scenario | Rekommenderat fönster | Varför |
|---|---|---|
| Utveckling/testning | 5 minuter | Spara pengar, kallstarter är okej |
| Internt API (ibland) | 10-15 minuter | Balans mellan kostnad och latens |
| Produktion (regelbunden trafik) | 20-30 minuter | Minimera kallstarter |
| Högtrafikproduktion | Använd min_containers=1 | Håll en alltid varm |
3. Välj rätt GPU
Välj inte alltid H100. Mindre modeller behöver den inte:
| Modellstorlek | Rekommenderad GPU | Ungefärlig kostnad/tim |
|---|---|---|
| 1-4B parametrar | L4 eller T4 | $0,59 – $0,80 |
| 7-8B parametrar | A10 eller L40S | $1,10 – $1,95 |
| 13-14B parametrar | A100 40 GB | $2,10 |
| 30-70B parametrar | A100 80 GB eller H100 | $2,50 – $3,95 |
| 70B+ parametrar | H100 x2 | $7,90 |
4. Aktivera prompt-cachning
Om dina arbetsbelastningar involverar upprepade systemprompter eller delade prefix kan vLLM:s automatiska prefixcachning avsevärt minska latens och beräkning. Aktivera det genom att lägga till --enable-prefix-caching till vLLM serve-kommandot. För en djupare genomgång av hur cachning fungerar hos olika leverantörer, kolla in vår guide om LLM-promptcachning.
5. Använd --enforce-eager för kallstartsoptimering
Som standard kompilerar vLLM CUDA-grafer vid uppstart, vilket tar 1-3 extra minuter. Flaggan --enforce-eager hoppar över den kompileringen. Du byter ~10-15% toppgenomströmning mot dramatiskt snabbare kallstarter. För burstig arbetsbelastning där latens är viktigare än rå genomströmning är det nästan alltid rätt val.
Att gå vidare: finjusterade modeller
När du är bekväm med att driftsätta basmodeller är nästa naturliga steg att driftsätta din egna finjusterade version. Arbetsflödet är identiskt — du pekar bara MODEL_NAME på ditt Hugging Face-repo eller en Modal-volym som innehåller dina finjusterade vikter.
Modal stödjer också att köra finjusteringsjobb direkt på deras GPU:er. Du kan träna en LoRA-adapter på Modal, spara den i en volym och driftsätta den sammanslagna modellen — allt utan att lämna plattformen. Vår guide om LLM-finjustering täcker träningssidan på djupet.
Den kompletta app.py
Här är det fullständiga driftsättningsskriptet i ett enda kopieringsklart block:
import modal
# --- Bilddefinition ---
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 modellcachning ---
hf_cache = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
vllm_cache = modal.Volume.from_name("vllm-cache", create_if_missing=True)
# --- Modellkonfiguration ---
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)Driftsätt med modal deploy app.py, byt ut MODEL_NAME mot vilken Hugging Face-modell som helst, och du är live.
Vanliga frågor
Hur mycket kostar det att köra en LLM på Modal?
Det beror på GPU:n och hur länge din endpoint förblir varm. En Qwen3-4B på en H100 kostar ~$3,95/tim aktiv användning. Med scale-to-zero och ett 15-minuters nedskaleringsfönster kan en lättutnyttjad endpoint kosta $5-15/månad. De $30 i gratis månadskrediter täcker en hel del experimentering.
Skalar Modal till noll?
Ja — det är en av dess viktigaste fördelar. När inga förfrågningar anländer under din scaledown_window-tid stängs containern av och du slutar betala. Nästa förfrågan utlöser en kallstart (vanligtvis 2-10 sekunder beroende på modellstorlek och om du använder --enforce-eager).
Kan jag driftsätta Llama 3.1 eller Mistral på Modal?
Absolut. Byt ut MODEL_NAME-konstanten mot vilken modell som helst som vLLM stödjer: meta-llama/Llama-3.1-8B-Instruct, mistralai/Mistral-7B-Instruct-v0.3, eller hundratals andra på Hugging Face. För 70B+-modeller, ändra N_GPU till 2 och använd gpu="H100:2".
Hur jämförs kallstarter med RunPod?
Modals kallstarter är typiskt 2-4 sekunder för själva containern, plus modellinläsningstid. Med modellvikter cachade i en Volume och --enforce-eager aktiverat handlar det om 10-30 sekunder totalt för en 7-8B-modell. RunPods serverlösa kallstarter sträcker sig från under 200 ms (cachad) till 6-12 sekunder för större containrar — men deras always-on-modell undviker kallstarter helt.
Är Modals vLLM-endpoint verkligen OpenAI-kompatibel?
Ja. vLLM implementerar samma /v1/chat/completions-, /v1/completions- och /v1/models-endpoints som OpenAI använder. Du kan peka den officiella openai Python SDK mot din Modal-URL och det fungerar direkt. Streaming, function calling och JSON-läge fungerar alla.
Behöver jag en GPU på min lokala maskin?
Nej. Din lokala maskin kör bara Modal CLI. Allt GPU-arbete sker på Modals molninfrastruktur. Du kan driftsätta från en Chromebook om du ville.
Hur lägger jag till autentisering till min endpoint?
Modal-webbendpoints är publika som standard. Lägg till en enkel API-nyckelkontroll i din applikationskod för produktion, eller använd Modals inbyggda webbautentiseringsfunktioner. Du kan också sätta upp ett proxylager med ett LLM-gateway som hanterar autentisering, hastighetsbegränsning och routing.
Vad är skillnaden mellan modal serve och modal deploy?
modal serve skapar en tillfällig endpoint som laddar om vid kodändringar — perfekt för utveckling. modal deploy skapar en persistent, produktionsklar endpoint med en stabil URL. Använd serve under iteration, deploy när du är redo att leverera.
Kan jag använda SGLang istället för vLLM?
Ja. Modals dokumentation innehåller SGLang-exempel vid sidan av vLLM. SGLang tenderar att ha mindre overhead för decode-tunga arbetsbelastningar och mindre modeller. vLLM är generellt sett bättre för blandade arbetsbelastningar med tung prefill. Båda producerar OpenAI-kompatibla endpoints.
Hur jämförs detta med att driftsätta på Railway eller Render?
Plattformar som Railway, Render och Fly.io är utmärkta för webbapplikationer, men de erbjuder inga GPU-instanser. Modal är byggd specifikt för GPU-arbetsbelastningar med sekundfakturering och autoskalning. Om du behöver serva en LLM är Modal (eller RunPod) rätt verktyg — traditionella PaaS-plattformar kan inte göra det.