Was dotnet.exe wirklich macht — und warum das für KMU meist irrelevant ist (aber nicht immer)

Steve Gordon erklärt tief im .NET-Innenleben, wie dotnet.exe die hostfxr-Bibliothek findet und lädt. Ich erkläre, warum das für 90 % der Mittelstands-Teams kein Thema ist — und wann es das doch wird.

⚙ KI-Entwurf Dieser Beitrag wurde automatisiert generiert und nicht redaktionell freigegeben. Behandelt ihn entsprechend.

Letzten Herbst saß ich bei einem Maschinenbauer in der Nähe von Stuttgart. 280 Mitarbeitende, eigene .NET-Entwicklung für Produktionssteuerung, drei Entwickler im Team. Ein Deployment auf den Produktionsserver schlug fehl — ohne brauchbare Fehlermeldung, nur ein kryptisches Exit-Code-Problem beim Start der Anwendung. Nach zwei Stunden Suche war das Problem klar: Auf dem Server standen zwei .NET-Laufzeiten nebeneinander, und dotnet.exe griff zur falschen. Der Entwickler hatte keine Ahnung, warum. Ich auch nicht sofort — weil ich nie genau nachgeschaut hatte, wie dieser Auswahlmechanismus intern funktioniert.

Das ist der Moment, für den Artikel wie der von Steve Gordon existieren.

Was Steve Gordon erklärt

Gordon taucht tief in den C++-Quellcode von dotnet.exe ein und zeigt Schritt für Schritt, wie der sogenannte Muxer die richtige Version der hostfxr-Bibliothek findet. Der Ablauf ist nüchtern und nachvollziehbar: dotnet.exe instanziiert einen hostfxr_resolver_t, der zunächst prüft, ob ein app-relativer Suchpfad eingebettet wurde — relevant für Self-Contained-Deployments. Im Normalfall (Framework-Dependent, also der typische Produktionsfall) sucht das System im Verzeichnis des Muxers selbst, hängt host\fxr an und liest dann alle vorhandenen Versionsverzeichnisse. Es gewinnt die höchste verfügbare Version, nicht die passendste — und genau da liegt Zündstoff.

Gordon schreibt selbst: Diese Posts sind kein Alltagswissen für Entwickler, sondern ein Werkzeug zum Verständnis tiefer Schichten. Das ist ehrlich. Und es ist genau das, was ich an seiner Serie schätze: Er hört auf zu erklären, wenn er erklärt hat, was da ist — ohne daraus ein Praxisrezept zu konstruieren, das keines ist.

Wie das im Mittelstand wirklich landet

Für die meisten KMU-Entwicklungsteams zwischen 30 und 300 Mitarbeitenden ist dieser Mechanismus solange unsichtbar wie ein funktionierendes Wasserrohr. Ihr deployt via Pipeline auf einen Windows-Server, dotnet.exe findet was es braucht, die App startet. Fertig. Niemand denkt an hostfxr_resolver_t.

Das Problem entsteht an drei konkreten Stellen:

1. Der Server-Zoo ohne Governance. Kein Plan, welche .NET-Versionen installiert sind. Jemand hat mal .NET 6 installiert für eine alte Anwendung, dann .NET 8 für die neue API, jetzt läuft beides nebeneinander. Wenn Gordon erklärt, dass get_latest_fxr immer die höchste installierte Version nimmt — nicht die, die euer Projekt erwartet — dann ist das genau die Ursache für Deployments, die auf dem Entwickler-Laptop funktionieren und auf dem Produktionsserver nicht. Ich sehe das in vielleicht 30 % der Mandate, bei denen ich zum ersten Mal auf die Server schaue.

2. Self-Contained vs. Framework-Dependent, falsch gewählt. Viele KMU-Teams wählen Framework-Dependent, weil es kleiner und schneller deployed ist. Das ist oft richtig — aber nicht, wenn auf dem Zielserver niemand die Runtime-Versionen kontrolliert. Gordons Erklärung zum eingebetteten EMBED_DOTNET_SEARCH_FULL_UTF8-Platzhalter zeigt: Bei Self-Contained-Deployments wird dieser Pfad vom SDK beim dotnet publish gesetzt, und der Resolver sucht gezielt dort. Das ist deterministische, beherrschbare Infrastruktur. Wer auf Servern deployt, die IT-seitig verwaltet werden und auf denen Entwickler keine Kontrolle über installierte Runtimes haben, sollte Self-Contained ernsthafter in Betracht ziehen — auch wenn das Artefakt größer wird.

3. Debugging ohne Grundlagenwissen. Der Stuttgarter Entwickler wusste nicht, wo er suchen sollte. Er hätte das Problem in zehn Minuten lösen können, wenn er gewusst hätte: dotnet.exe schaut in C:\Program Files\dotnet\host\fxr, nimmt das höchste Versionsverzeichnis, und von dort geht es weiter. Dieses eine Bild — das Gordon durch den Code-Walkthrough erzeugt — ist Debugging-Wissen, das drei Stunden Ursachensuche verhindert.

Was in Gordons Artikel naturgemäß fehlt: Der organisatorische Kontext. Er schreibt aus .NET-Architekt-Sicht, und das zu Recht. Aber im Mittelstand ist die Frage nicht nur "wie funktioniert das technisch", sondern "wer ist verantwortlich dafür, dass auf diesem Server die richtige Runtime steht". Die Antwort ist erschreckend oft: niemand konkret. Entwicklung sagt "das ist IT-Thema", IT sagt "das ist Entwickler-Thema", und am Ende deployt jemand mit Admin-Rechten irgendetwas von der .NET-Download-Seite.

Konkrete Empfehlung

Drei Dinge, die ich nach Mandaten wie dem in Stuttgart immer anschiebe:

Inventur machen. Einmal dotnet --list-runtimes und dotnet --list-sdks auf jedem Produktionsserver laufen lassen und das Ergebnis dokumentieren. Klingt trivial, ist es aber nicht — weil es niemand macht. Das Ergebnis zeigt sofort, ob ihr einen Server-Zoo habt.

Runtime-Version explizit pinnen. In der runtimeconfig.json bzw. im Projekt <TargetFramework> klar definieren, und im Deployment sicherstellen, dass genau diese Runtime auf dem Zielserver vorhanden ist — nicht irgendeine höhere. Gordons Analyse zeigt, dass dotnet.exe ohne explizite Steuerung immer zur höchsten Version greift. Das ist meistens harmlos. Meistens.

Für unkontrollierte Server: Self-Contained evaluieren. Wenn ihr in Umgebungen deployt, in denen ihr die installierten Runtimes nicht steuert — Kundensysteme, Legacy-Windows-Server, Industrieumgebungen — ist Self-Contained kein Overhead, sondern Risikominimierung. Ja, das Artefakt ist 60-80 MB größer. Nein, das spielt im Jahr 2026 keine Rolle mehr.

Was ich nicht empfehle: Dass jeder Entwickler im KMU-Team jetzt Gordons Artikel liest und sich durch den C++-Quellcode arbeitet. Das ist Vertiefungswissen für die Person im Team, die Deployments verantwortet, und für alle anderen eine sinnvolle Referenz, wenn ein Problem auftaucht, das sich nicht anders erklären lässt. Exakt dafür hat Gordon diesen Artikel geschrieben — und exakt dafür ist er gut.

Das Wissen über hostfxr_resolver_t und get_latest_fxr wird euch nicht täglich nützen. Aber in dem Moment, wo euer Deployment auf dem Produktionsserver schweigend stirbt, ist es der Unterschied zwischen drei Stunden Raten und zehn Minuten gezielter Diagnose.


Der vollständige Original-Beitrag von Steve Gordon: How dotnet.exe resolves and loads the hostfxr library – Exploring the .NET muxer


Wenn ihr gerade eine Deployment-Situation habt, die ihr nicht ganz einordnen könnt — oder wenn ihr wissen wollt, ob eure Server-Infrastruktur überhaupt in Ordnung ist — dann redet mit mir direkt.

30 Min Klartext-Sparring

— Bernhard