Så kör du en egen AI-gateway på Kubernetes för self-hosted LLM:er
- Goran Nushkov

- för 6 dagar sedan
- 11 min läsning
Just nu pågår en allt tydligare diskussion i Europa om AI-suveränitet och det finns goda skäl till det. När du skickar prompts till en hostad AI-tjänst lämnar din data ditt nätverk, passerar genom någon annans infrastruktur och behandlas enligt villkor du kanske inte har läst tillräckligt noggrant. I många situationer är det helt okej. I andra är det inte det.
Det här inlägget kommer inte att säga att du ska skrota moln-AI helt. Hostade modeller är smidiga, snabba och, ärligt talat, imponerande. Men om du jobbar med känslig information, verkar i en reglerad bransch eller helt enkelt vill ha full kontroll över vad som körs i din egen miljö, är det värt att förstå hur en egen AI-gateway fungerar.
I praktiken handlar det om att bygga en privat AI-infrastruktur, där språkmodeller körs i din egen miljö i stället för att anropas som externa tjänster. Vi går igenom exakt hur du gör det, från en lokal uppsättning till en produktionsdriftsättning på Kubernetes.
Vad vi ska sätta upp
Kärnan i lösningen är llama.cpp, närmare bestämt dess serverbinär llama-server. Den kan ladda kvantiserade språkmodeller och exponera ett OpenAI-kompatibelt API på valfri port.
Det är viktigt: allt som redan fungerar mot OpenAI-API:et fungerar här också, utan att du behöver ändra en enda rad kod. Du pekar bara klienten mot en annan base URL.
Med andra ord kör du i praktiken ditt eget self-hosted OpenAI-kompatibla API i din egen infrastruktur.
Dessutom kommer llama-server med ett inbyggt webbgränssnitt på samma port. Du får ett chattgränssnitt direkt, utan extra tjänster.
En endpoint, flera modeller, noll leverantörsberoende.
En notering om hårdvara och förväntningar
Var realistisk. Att köra stora språkmodeller lokalt kräver rejäl hårdvara. De modeller som ger verkligt bra resultat ligger ofta i spannet 14B till 35B parametrar och de kräver antingen en kapabel GPU, mycket RAM eller båda.
Samtidigt har kvantiserade modeller blivit riktigt bra. En Q4-kvantiserad 35B-modell kan fungera fint på ett konsument-GPU med 24 GB VRAM och den kan även köras på CPU med tillräckligt mycket RAM, bara långsammare. Poängen är inte att alla ska göra detta, utan att det är mer tillgängligt än det var tidigare.
För organisationer som tittar på on-prem LLM-drift eller säker AI-infrastruktur gör det här upplägget det möjligt att köra kraftfulla modeller utan att data behöver lämna det egna nätverket.
När det gäller kvalitet: lokala modeller är bra. Inte perfekta, och inte alltid i nivå med de allra senaste hostade modellerna, men absolut användbara när du matchar rätt modell mot rätt uppgift. En kodassistent, en sammanfattare eller en pipeline för strukturerad dataextraktion fungerar bra med rätt modell och rätt konfiguration.
Modellrekommendationer
Vilken modell du ska välja beror på vad du vill göra och vilken hårdvara du har. Här är en praktisk genomgång.
En ärlig startpunkt: Qwen3.5
Om du är osäker på var du ska börja: börja med Qwen3.5. Det är en modellfamilj som täcker det mesta – resonemang, vision och tool calling – i storlekar från 0.8B upp till 397B.
MoE-varianterna (Mixture of Experts) aktiverar bara en del av parametrarna per inferens, vilket gör dem snabbare och mer minneseffektiva än parameterantalet antyder. Fullständiga detaljer och rekommenderade inställningar finns i Unsloth Qwen3.5-dokumentationen, som är värd att läsa innan du börjar ladda ner modeller.
Här är vad du faktiskt behöver i minne beroende på storlek och kvantisering (minnet avser totalen: VRAM + systemminne, eller unified memory på Apple Silicon):
Model size | 4-bit | 6-bit | 8-bit |
0.8B + 2B | 3.5 GB | 5 GB | 7.5 GB |
4B | 5.5 GB | 7 GB | 10 GB |
9B | 6.5 GB | 9 GB | 13 GB |
27B | 17 GB | 24 GB | 30 GB |
35B-A3B | 22 GB | 30 GB | 38 GB |
122B-A10B | 70 GB | 106 GB | 132 GB |
35B-A3B är en bra sweet spot för de flesta mer krävande användningsområden. I 4-bitarsformat kräver den omkring 22 GB totalt minne, får plats på ett enda GPU med 24 GB VRAM och levererar riktigt starka resultat.
Den täta 27B-modellen är något mer exakt men också långsammare. 9B- och 4B-modellerna är bra alternativ när hårdvaran är den begränsande faktorn.
Thinking mode
Qwen3.5 är en hybridmodell: den kan antingen “tänka igenom” ett problem steg för steg innan den svarar, eller svara direkt. För större storlekar (27B och uppåt) är thinking-läget på som standard. För mindre (0.8B, 2B, 4B, 9B) är det av som standard. Du styr detta per request eller globalt med en flagga:
--chat-template-kwargs '{"enable_thinking":true}' # enable
--chat-template-kwargs '{"enable_thinking":false}' # disable
Thinking-läge höjer kvaliteten på svårare uppgifter, men ökar tokenförbrukningen och svarstiden tydligt. För exempelvis en kodassistent eller en pipeline för strukturerad extraktion är det ofta rimligt att ha det av om du inte faktiskt behöver resonemanget.
Rekommenderade parameterinställningar
De rätta parametrarna beror både på modellstorleken och på om thinking-läget är aktiverat. För thinking-läge rekommenderar Unsloth:
Parameter | General tasks | Precise coding |
temperature | 1.0 | 0.6 |
top_p | 0.95 | 0.95 |
top_k | 20 | 20 |
min_p | 0.0 | 0.0 |
presence_penalty | 1.5 | 0.0 |
repeat_penalty | 1.0 | 1.0 |
För non-thinking (instruct) mode:
Parameter | General tasks | Reasoning tasks |
temperature | 0.7 | 1.0 |
top_p | 0.8 | 0.95 |
top_k | 20 | 20 |
min_p | 0.0 | 0.0 |
presence_penalty | 1.5 | 1.5 |
repeat_penalty | 1.0 | 1.0 |
Det maximala kontextfönstret är 262 144 tokens och kan vid behov utökas ytterligare via YaRN. För de flesta arbetslaster räcker 32 768 tokens i outputlängd mer än väl.
Vision-uppgifter
Modeller som stöder bildinmatning levereras med en separat mmproj-fil tillsammans med huvudfilen i GGUF-format. Båda monteras i din konfiguration. Qwen3.5 35B A3B stöder vision på detta sätt.
Om kvantisering
Q4_K_XL är ett stabilt standardval. Det ger en bra balans mellan storlek, minnesanvändning och kvalitet på modellens svar.
Går du under Q4 börjar kvaliteten försämras märkbart. Q8 ligger nära full precision men använder ungefär dubbelt så mycket minne.
Var du hittar modeller
Hugging Face är den huvudsakliga källan. Du ska använda filer i GGUF-format, vilket är det format llama.cpp laddar direkt.
Unsloth producerar särskilt väloptimerade GGUF-kvantiseringar som är mindre, snabbare och noggrant justerade. Det är värt att titta där innan du laddar ner en slumpmässig modell.
Metoden med konfigurationsfil
I stället för att köra en container per modell eller hantera flera olika kommandon stöder llama-server en konfigurationsfil där alla modeller definieras på ett ställe. Servern laddar dem och exponerar varje modell som en namngiven endpoint på samma port.
En sak som är bra att känna till: du kan definiera samma modell flera gånger under olika namn med olika parameterinställningar. Det gör det möjligt att exponera samma modell både i thinking-läge och icke-thinking-läge, eller med olika temperaturprofiler för olika användningsområden, utan att ladda modellen två gånger.
Här är ett exempel på en konfiguration som täcker några praktiska scenarier:
; Qwen3.5 35B A3B: thinking mode, general tasks
[qwen3.5-35b-thinking]
model = /models/qwen3.5-35b-a3b/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf
mmproj = /models/qwen3.5-35b-a3b/mmproj-F16.gguf
ngl = 99
ctx-size = 131072
temp = 1.0
top-p = 0.95
top-k = 20
min-p = 0.0
presence-penalty = 1.5
repeat-penalty = 1.0
flash-attn = on
chat-template-kwargs = {"enable_thinking":true}
; Qwen3.5 35B A3B: non-thinking mode, precise coding
[qwen3.5-35b-coding]
model = /models/qwen3.5-35b-a3b/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf
mmproj = /models/qwen3.5-35b-a3b/mmproj-F16.gguf
ngl = 99
ctx-size = 131072
temp = 0.6
top-p = 0.95
top-k = 20
min-p = 0.0
presence-penalty = 0.0
repeat-penalty = 1.0
flash-attn = on
chat-template-kwargs = {"enable_thinking":false}
; Qwen3.5 9B: lightweight, non-thinking, general use
[qwen3.5-9b]
model = /models/qwen3.5-9b/Qwen3.5-9B-UD-Q4_K_XL.gguf
ngl = 99
ctx-size = 32768
temp = 0.7
top-p = 0.8
top-k = 20
min-p = 0.0
presence-penalty = 1.5
repeat-penalty = 1.0
flash-attn = on
chat-template-kwargs = {"enable_thinking":false}
Några saker som är bra att känna till
ngl är antalet lager som flyttas över till GPU:n. Sätt värdet till 99 för att flytta över allt som får plats.
ctx-size är kontextfönstret i tokens. Ett större värde gör att modellen kan hantera längre konversationer eller dokument, men kräver mer minne.
flash-attn = on aktiverar Flash Attention, vilket minskar minnesanvändningen och snabbar upp inferensen.
mmproj behövs bara för modeller som stöder bildinmatning. Den pekar på den multimodala projektorfilen som levereras tillsammans med modellen.
chat-template-kwargs används för att skicka inställningar för thinking-läge och andra mallrelaterade parametrar för varje modell.
Var modellfilerna ligger
llama-server läser modellfiler från disk vid uppstart och laddar dem i minnet. Det innebär att filerna måste vara tillgängliga i filsystemet som containern ser, som en monterad sökväg.
De behöver dock inte ligga på samma fysiska maskin som noden. Så länge volymen är monterad i podden och är läsbar behandlar llama-server den som en lokal sökväg.
I praktiken betyder det att du kan lagra modellfiler på en dedikerad lagringsserver eller en nätverksdelning och montera dem i podden via NFS, en distribuerad lagringslösning eller någon annan nätverksbaserad volym.
Podden märker ingen skillnad och det gör inte llama-server heller. Detta beskrivs mer i detalj i Kubernetes-delen nedan.
Kör lokalt först
Innan du deployar något är det värt att köra servern lokalt för att få en känsla för hur den fungerar.
Installationen beror helt på vilken hårdvara du har. CPU-only, NVIDIA CUDA, AMD ROCm eller Apple Silicon med Metal har alla sina egna byggsteg.
I stället för att duplicera dokumentation som redan finns är det bättre att gå till llama.cpp build-dokumentationen och följa instruktionerna för din specifika miljö. Det är värt att läsa ordentligt i stället för att bara skumma igenom rätt build för din hårdvara är skillnaden mellan snabb inferens och mycket långsam inferens.
När du har byggt eller installerat llama-server pekar du den mot din konfigurationsfil:
llama-server --port 8080 --models-preset ./config.ini
När servern startar öppnar du http://localhost:8080 i webbläsaren. Där ser du det inbyggda chattgränssnittet där du kan välja vilken modell som ska användas och börja direkt.
Samma port exponerar också det OpenAI-kompatibla API:et på:
/v1/chat/completionsDocker
De officiella llama.cpp-Docker images publiceras på ghcr.io/ggml-org/llama.cpp. För vårt syfte är server image den vi vill ha. Det finns varianter för CUDA (NVIDIA), ROCm (AMD), Vulkan och Intel SYCL beroende på din hårdvara.
En enkel Docker Compose-setup kan se ut så här:
services:
llama-server:
ports:
- "8080:8080"
volumes:
- /path/to/models:/models
- ./config.ini:/config/config.ini
command:
- --port
- "8080"
- --host
- "0.0.0.0"
- --models-preset
- /config/config.ini
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
Om du kör CPU-only, använd den vanliga server image och ta bort deploy.resources-blocket. Flaggan --host 0.0.0.0 är viktig om du vill att servern ska gå att nå utanför containern.
Deploying on Kubernetes
För en produktionsdrift följer här råa Kubernetes manifests för att få gatewayen att köra.
Att köra LLM inference on Kubernetes på det här sättet blir ett allt vanligare mönster för organisationer som bygger interna AI-plattformar eller private AI infrastructure.
De är tillräckligt raka för att förstå och anpassa, och fungerar som en stabil startpunkt om du senare vill paketera dem i ett Helm chart eller en Kustomize overlay.
Du behöver ett fungerande kluster och kubectl konfigurerat. För GPU-stöd behöver du också NVIDIA device plugininstallerat i klustret. Det är det som gör nvidia.com/gpu till en schemaläggningsbar resurs i Kubernetes.
I praktiken fungerar den här setupen som en intern AI platform endpoint som applikationer kan anropa via ett self-hosted OpenAI-compatible API.
Utan pluginet kommer GPU resource request i Deployment inte att matcha och podden kommer inte att schemaläggas. Om du kör CPU-only kan du ta bort resursblocket helt.
Namespace
Börja med ett dedikerat namespace för att hålla det snyggt:
kubectl create namespace ai
ConfigMap
Model config monteras i podden som en ConfigMap. Det håller den separat från container image och gör den enkel att uppdatera utan att bygga om något:
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: llama-config
namespace: ai
data:
config.ini: |
; Qwen3.5 35B A3B: thinking mode, general tasks
[qwen3.5-35b-thinking]
model = /models/qwen3.5-35b-a3b/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf
mmproj = /models/qwen3.5-35b-a3b/mmproj-F16.gguf
ngl = 99
ctx-size = 131072
temp = 1.0
top-p = 0.95
top-k = 20
min-p = 0.0
presence-penalty = 1.5
flash-attn = on
chat-template-kwargs = {"enable_thinking":true}
; Qwen3.5 35B A3B: non-thinking mode, precise coding
[qwen3.5-35b-coding]
model = /models/qwen3.5-35b-a3b/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf
mmproj = /models/qwen3.5-35b-a3b/mmproj-F16.gguf
ngl = 99
ctx-size = 131072
temp = 0.6
top-p = 0.95
top-k = 20
min-p = 0.0
presence-penalty = 0.0
flash-attn = on
chat-template-kwargs = {"enable_thinking":false}
Deployment
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: llama-gateway
namespace: ai
spec:
replicas: 1
selector:
matchLabels:
app: llama-gateway
template:
metadata:
labels:
app: llama-gateway
spec:
containers:
- name: llama-server
args:
- --port
- "8080"
- --host
- "0.0.0.0"
- --models-preset
- /config/config.ini
ports:
- containerPort: 8080
resources:
requests:
limits:
volumeMounts:
- name: config
mountPath: /config
- name: models
mountPath: /models
volumes:
- name: config
configMap:
name: llama-config
- name: models
# hostPath is the simplest option for single-node setups.
# Replace with a PVC backed by your StorageClass for anything beyond that.
# (NFS provisioner, Longhorn, Rook/Ceph, cloud block storage, etc.)
hostPath:
path: /data/models
type: Directory
# Adjust this label to match what your cluster actually uses.
# Remove entirely if running CPU only.
nodeSelector:
nvidia.com/gpu: "true"
hostPath-volymen är den enklaste startpunkten, men för riktiga driftsättningar vill du normalt ha en PersistentVolumeClaim backad av en riktig StorageClass: en NFS provisioner, Longhorn, Rook/Ceph eller det som klustret redan använder.
Modellfiler är stora men read-only vid körning, vilket gör dem lämpliga för ReadOnlyMany om din storage backend stödjer det.
nodeSelector ser till att podden bara hamnar på en nod som faktiskt har en GPU.
Justera labeln så den matchar det ni använder i klustret.
Service
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: llama-gateway
namespace: ai
spec:
selector:
app: llama-gateway
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
Ingress (optional)
Om du vill kunna nå gatewayen internt i nätverket utan port-forwarding kan du lägga till en Ingress.
Detta förutsätter att du har en ingress controller, till exempel ingress-nginx, som kör:
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: llama-gateway
namespace: ai
spec:
ingressClassName: nginx
rules:
- host: llm.your-domain.internal
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: llama-gateway
port:
number: 8080
Applicera konfigurationen
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml # if using ingress
Kontrollera att allt kör
kubectl get pods -n ai
kubectl logs -n ai deployment/llama-gateway
Följ loggarna när servern startar. När du ser att den lyssnar är gatewayen uppe.
Du kan då antingen nå den via Ingress host eller port-forwarda för att testa lokalt:
kubectl port-forward -n ai svc/llama-gateway 8080:8080
Öppna sedan http://localhost:8080 för chat UI, eller använd http://localhost:8080/v1/chat/completions som din API endpoint.
Härifrån är de här manifesten en naturlig startpunkt för ett Helm chart eller en Kustomize overlay om du vill hantera flera miljöer eller göra konfigurationen mer återanvändbar.
Vad du kan använda det till
När gatewayen kör kan allt som använder OpenAI API format prata med den. Du ändrar bara base URL och kan (om du vill) sätta en dummy API key (llama-server kräver ingen som standard, men vissa klienter insisterar på att skicka en).
Agentic coding
Verktyg som OpenCode, Cursor och Continue stödjer custom OpenAI-compatible endpoints. Peka dem mot din gateway, välj din coding model, och du har en helt lokal coding assistant utan token costs och utan att data lämnar ditt nätverk.
Chat interfaces
Open WebUI ansluter direkt till din endpoint och ger ett polerat chat interface med conversation history, model switching och mer, om du vill ha något utöver built-in UI.
Applications
Om du bygger något internt, oavsett om det är en document processor, en support bot eller en data extraction pipeline, kan du använda OpenAI SDK och peka den mot din egen endpoint. Bytet är en rad i konfigurationen.
Automation and agents
Frameworks som LangChain, LlamaIndex och AutoGen stödjer alla custom base URLs. Dina interna agents kan köras helt i din egen infrastruktur.
Wrapping Up
Den här setupen är inte för alla. Du behöver hårdvara, du behöver hantera model files och du behöver hålla saker uppdaterade. Modellerna är bra, men de är inte magi, och att matcha rätt modell mot rätt uppgift kräver en del experimenterande.
Men om du vill ha kontroll, om du hanterar data som inte bör lämna din miljö, eller om du bara är nyfiken på hur det faktiskt ser ut att köra en egen AI gateway i praktiken, är det här ett stabilt, produktionsredo upplägg.
För team som bygger enterprise AI platforms eller secure AI infrastructure ger en egen gateway ett praktiskt sätt att deploya LLMs on Kubernetes utan att introducera externa beroenden.
OpenAI compatibility betyder att du inte låser in dig i något nytt – du ändrar bara var anropen går.
Modellerna utvecklas snabbt och tooling runt llama.cpp har mognat rejält. Om hårdvaran finns och behovet passar är det här ett produktionsklart alternativ redan idag, inte ett science project.
Om du tittar på liknande upplägg och behöver hjälp att designa eller deploya en private AI infrastructure arbetar våra konsulter med Kubernetes platforms, LLM deployments och secure AI environments.
Hör av dig om du vill diskutera hur det skulle kunna se ut i er miljö.




Kommentarer