Startseite / Artikel / Ollama auf WSL2 meldet „Connection Refused“: Fehler beheben und Dify sowie vLLM anbinden

Ollama auf WSL2 meldet „Connection Refused“: Fehler beheben und Dify sowie vLLM anbinden

Ollama läuft in WSL2, aber Dify bekommt Connection Refused? Bind-Adresse prüfen, OLLAMA_HOST setzen, einen portproxy anlegen und beim Anbinden von vLLM die Docker-localhost-Falle umgehen.

1543 Wörter

Ollama lief einwandfrei auf einem Windows-11-Rechner. Man konnte ein Modell ziehen und direkt im WSL2-Terminal damit chatten. Sobald aber ein anderer Dienst darauf zugreifen wollte, wurde die Verbindung abgelehnt. Die Modelle waren nicht das Problem, sondern das Netzwerk.

Im Folgenden geht es um den Weg von diesem kaputten, isolierten Setup zu einem, das Neustarts übersteht und lokale Modelle über mehrere virtuelle Grenzen hinweg bereitstellt.

Das Ziel und wo es scheiterte

Ziel war ein lokaler KI-Entwicklungsstack für Leute, die mit LLMs arbeiten wollen, ohne private Daten an einen Cloud-Anbieter zu schicken. Ollama sollte schnelle Experimente und lokale RAG-Workflows abdecken. vLLM sollte schwerere Inferenz mit hohem Durchsatz übernehmen. Beide sollten dann an Dify angebunden werden, einen Open-Source-Baukasten für LLM-Apps, der in Docker läuft.

Die ersten Schritte liefen glatt. Ollama wurde in einer Ubuntu-Distribution unter WSL2 installiert, das Modell llama3 gezogen, die CLI funktionierte, und eine Testanfrage an die lokale API aus derselben Linux-Shell kam sofort zurück.

Dann meldete Dify, das in einem Docker-Container im Host-Netzwerk lief, einen Verbindungsfehler. Erster Verdacht war die Dify-Konfiguration, und dieser Verdacht war falsch. Die eigentliche Ursache war ein Missverständnis darüber, wie sich ein Dienst an eine Netzwerkschnittstelle bindet, verschärft durch die getrennten Netzwerk-Namespaces, die WSL2 und Docker jeweils anlegen.

Stellen Sie sich den Aufbau als verschachtelte Räume vor. Außen steht der Windows-11-Host. Docker Engine bildet einen Namespace, darin die Container Dify API und Dify worker. Die WSL2-Utility-VM bildet einen weiteren, mit Ollama auf Port 11434 und vLLM auf Port 8000. Zwei Grenzen zählen: die zwischen Windows und der WSL2-VM und die zwischen dem Docker-Netzwerk und dem Host. Genau an diesen Wänden wohnt die berüchtigte localhost-Verwirrung.

Läuft heißt nicht erreichbar

Ein gesunder Dienst ist nicht unbedingt ein erreichbarer. Ollama lebte, aber das hieß nur, dass sein Prozess lief und an eine bestimmte Schnittstelle tief im Linux-Kernel gebunden war.

Ein Server lauscht nicht abstrakt auf „das Netzwerk“. Er bindet sich an eine bestimmte IP-Adresse und einen Port, und ein Client kommt nur durch, wenn er Pakete an eine routbare Adresse schickt, die zu dieser Bindung passt. Viele sichere Standardeinstellungen binden an 127.0.0.1, das IPv4-Loopback, und sperren den Dienst damit in genau den Namespace ein, in dem er gestartet wurde. Die Bindung an 0.0.0.0 macht ihn auf allen Schnittstellen der Maschine erreichbar, und das ist Pflicht, wenn WSL2 mit Windows oder Docker verbunden werden soll.

Die abgelehnte Verbindung aufspüren

Wenn etwas Ollama nicht erreicht, klären Sie zuerst, welche Schicht versagt: DNS, TCP oder die Anwendung. Hier war das Symptom ein Verbindungsfehler in Dify. Die Hypothese: ein gescheiterter TCP-Handshake über die VM-Grenze. Der Test: eine rohe TCP-Prüfung von der Windows-Seite, die in einem anderen Namespace als WSL2 liegt und jede Anwendungslogik umgeht. In PowerShell:

Test-NetConnection -ComputerName localhost -Port 11434

Das Ergebnis war TcpTestSucceeded : False, der Handshake scheiterte also und Dify war nicht schuld. Als Nächstes sehen Sie sich den lauschenden Socket aus Linux heraus an, im WSL2-Terminal:

ss -tlnp | grep 11434

Die Ausgabe brachte Klarheit:

LISTEN 0  4096  127.0.0.1:11434  0.0.0.0:*  users:((ollama,pid=1234,fd=7))

Laut der ss-Ausgabe lauschte Ollama nur auf 127.0.0.1:11434. Alles, was über die virtuelle Ethernet-Schnittstelle von WSL2 hereinkam, über die Windows mit der VM spricht, lehnte der Kernel sofort ab.

Drei Begriffe, die man sauber trennen sollte

localhost ist nur ein Hostname, der über die hosts-Datei aufgelöst wird. Auf welche Maschine er tatsächlich zeigt, hängt davon ab, in welchem Container oder welcher VM Sie ihn gerade eintippen.

127.0.0.1 ist die IPv4-Loopback-Adresse. Sie ist eine virtuelle Schnittstelle innerhalb eines einzelnen OS-Kernels, und Pakete an sie erreichen nie eine physische oder virtuelle Netzwerkkarte.

Dann gibt es die Wildcard 0.0.0.0 (INADDR_ANY). Bindet sich ein Server daran, nimmt das Betriebssystem eingehende TCP-Verbindungen auf allen seinen IPv4-Schnittstellen an.

Die WSL-Netzwerkdokumentation von Microsoft unter https://learn.microsoft.com/en-us/windows/wsl/networking erklärt, dass der Hypervisor eine schlanke Utility-VM mit eigenem Netzwerkadapter und eigener NAT-Adresse betreibt, sodass Windows dieses NAT durchqueren muss, um die Linux-Seite zu erreichen.

Die eine Änderung, die es behebt

Bringen Sie Ollama dazu, sich an alle Schnittstellen zu binden, indem Sie die Umgebungsvariable OLLAMA_HOST setzen. Wenn systemd den Prozess verwaltet, ändern Sie nicht die Haupt-Unit-Datei, denn Paket-Updates überschreiben Ihre Änderung. Legen Sie stattdessen ein Drop-in-Override an. Erstellen Sie in WSL2 zuerst das Verzeichnis:

# Create the override directory
sudo mkdir -p /etc/systemd/system/ollama.service.d

Schreiben Sie dann die Override-Datei:

# Create and edit the configuration file safely
echo '[Service]' | sudo tee /etc/systemd/system/ollama.service.d/override.conf
echo 'Environment=OLLAMA_HOST=0.0.0.0' | sudo tee -a /etc/systemd/system/ollama.service.d/override.conf# Reload systemd and restart the daemon
sudo systemctl daemon-reload
sudo systemctl restart ollama

Prüfen Sie nach einem Neustart erneut den Socket. So sieht Erfolg aus:

LISTEN 0  4096  *:11434  *:*  users:((ollama,pid=1234,fd=7))

Das Sternchen bedeutet, dass auf allen Schnittstellen gelauscht wird, und der wiederholte Netzwerktest von Windows ergab jetzt True.

Warum das Einfügen der WSL2-IP eine Falle ist

Zunächst wurde Dify auf Ollama gerichtet, indem man ip addr show eth0 in WSL2 ausführte, die Adresse kopierte und in die Dify-Einstellungen einfügte, etwa http://172.20.5.42:11434. Das funktionierte eine Weile und ist ein fragiles Design. Der virtuelle Hyper-V-Switch vergibt diese Adresse dynamisch, sie ändert sich also nach jedem Neustart oder Dienst-Restart und bricht die Integration stillschweigend.

Robuster ist es, den Windows-Host den Verkehr per netsh portproxy deterministisch weiterleiten zu lassen. Damit das ohne Handarbeit Neustarts übersteht, nutzen Sie ein PowerShell-Skript, das auf dem Host als Administrator läuft. Der erste Teil ermittelt die aktuelle NAT-Adresse der WSL2-Instanz:

# Dynamically fetch the current WSL2 NAT IP
$wsl_ip = (wsl -d Ubuntu -e sh -c 'ip -4 addr show eth0 | grep -oP ''(?<=inet\s)\d+(\.\d+){3}''')

Der zweite Teil entfernt veraltete Routing-Regeln und legt neue an, die auf die aktuelle Adresse zeigen:

# Clear existing stale routing rules
netsh interface portproxy delete v4tov4 listenport=11434 listenaddress=0.0.0.0
netsh interface portproxy delete v4tov4 listenport=8000 listenaddress=0.0.0.0# Add the new dynamic rules for Ollama (11434) and vLLM (8000)
netsh interface portproxy add v4tov4 listenport=11434 listenaddress=0.0.0.0 connectport=11434 connectaddress=$wsl_ip
netsh interface portproxy add v4tov4 listenport=8000 listenaddress=0.0.0.0 connectport=8000 connectaddress=$wsl_ipWrite-Host 'Port forwarding established to WSL2 IP: ' $wsl_ip

Damit landen localhost-Anfragen unter Windows auf der IP, die die Linux-Instanz gerade hat. Speichern Sie das Skript, lassen Sie es von der Windows-Aufgabenplanung beim Start ausführen, und die manuelle IP-Buchhaltung entfällt.

vLLM dazuholen

Mit stabilem Ollama war vLLM an der Reihe. Sein PagedAttention-Design bringt einen großen Durchsatzvorteil, wenn viele Generierungen gleichzeitig laufen. Der Server wurde in WSL2 mit gesetztem Host-Flag, freigegebenem Port und vorsichtig bemessenem GPU-Speicher gestartet, um Abstürze wegen Speichermangels zu vermeiden:

python -m vllm.entrypoints.openai.api_server \
  --model mistralai/Mistral-7B-Instruct-v0.3 \
  --host 0.0.0.0 \
  --port 8000 \
  --gpu-memory-utilization 0.8

Bevor Sie die Dify-Oberfläche öffnen, prüfen Sie die API-Route für sich allein. Anwendungsfehler mit Netzwerkfehlern zu verwechseln, kann Stunden kosten. JSON-Payloads brauchen doppelte Anführungszeichen, was schnelle Bash-Einzeiler umständlich macht, also erledigt ein kurzes Python-Skript den Job. Es beginnt mit dem Import:

import requests

und geht mit der eigentlichen Anfrage weiter:

api_url = 'http://localhost:8000/v1/chat/completions'
headers = {'Content-Type': 'application/json'}
payload = {
    'model': 'mistralai/Mistral-7B-Instruct-v0.3',
    'messages': [{'role': 'user', 'content': 'Explain network routing briefly.'}]
}response = requests.post(api_url, headers=headers, json=payload)
print(response.status_code)
print(response.json())

Ein HTTP 200 bedeutet, dass vLLM als OpenAI-kompatibler Server funktioniert. Wäre dieser Aufruf gescheitert, wüsste man, dass das Problem komplett in der Linux-VM liegt.

Die Docker-localhost-Falle

Letzte Hürde: Innerhalb eines Containers bezeichnet localhost den Container selbst. Wer in Dify http://localhost:8000/v1 einträgt, lässt den Dify-API-Container im eigenen Namespace nach einem Modell suchen, und das scheitert sofort.

Da der Windows-portproxy die KI-Dienste bereits auf dem Host bereitstellt, braucht der Container nur einen Weg hinaus zum Host. Docker Desktop bietet dafür einen speziellen DNS-Namen, beschrieben unter https://docs.docker.com/desktop/networking/. Öffnen Sie in Dify Model Provider, wählen Sie OpenAI-API-compatible und tragen Sie ein:

  • Endpoint: http://host.docker.internal:8000/v1
  • Model: was vLLM bereitstellt, hier mistralai/Mistral-7B-Instruct-v0.3
  • Key: wörtlich EMPTY, weil lokale Instanzen ihn nicht prüfen

Das Setup, das bleiben sollte

Der endgültige Ablauf sieht so aus. Eine Anfrage verlässt den Dify-Container in Richtung host.docker.internal:11434. Der Windows-Host empfängt sie, und die netsh-portproxy-Regel greift. Die Regel leitet sie an die 172.x.x.x-Adresse des virtuellen WSL2-NAT weiter, wo der an 0.0.0.0 gebundene Ollama-Daemon die Payload annimmt.

Dieses Setup beendet das manuelle Hantieren mit IPs, und die Brücke zwischen Host-Betriebssystem, Hypervisor und Container-Engine verhält sich jetzt jeden Tag gleich. Zu wissen, wie diese Namespaces zusammenpassen, hilft nicht nur beim Reparieren einer wackligen Verbindung, sondern ist die Grundlage für eine verlässliche lokale KI-Infrastruktur.