Axum, Actix Web oder Rocket: welches passt?
Die drei unterscheiden sich weniger im Durchsatz als in der Frage, wie viel ihr selbst zusammensetzt und wie ruhig die Schnittstelle über die Jahre geblieben ist.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Die Frage nach dem Framework kommt meist eine Frage zu spät
Wer nach dem passenden Rust-Framework sucht, hat die Entscheidung für Rust schon getroffen, oft ohne sie an einer Kennzahl festzumachen. Genau dort steckt aber der größere Hebel. Ein Dienst, der fünfundneunzig Prozent seiner Antwortzeit damit verbringt, auf die Datenbank zu warten, wird durch einen Sprachwechsel nicht spürbar schneller. Der Gewinn liegt woanders: bei Diensten, die dauerhaft laufen und eine zugesagte Antwortzeit einhalten müssen, bei hohen Anfragezahlen, bei denen die Rechenzeit je Anfrage direkt Geld kostet, und bei teuren Schritten im Antwortpfad wie Bildverarbeitung, Kompression oder Kryptografie.
Die zweite Lücke betrifft das, was Vergleichstabellen im Netz messen. Fast alle bekannten Übersichten stellen Anfragen pro Sekunde nebeneinander, und dort liegen die drei Frameworks so dicht beieinander, dass die Zahl für eine Entscheidung unbrauchbar ist. Der reale Unterschied liegt in der Frage, wie viel ihr selbst zusammensetzen müsst, wie viel vom übrigen Ökosystem ihr wiederverwenden könnt und wie oft eine neue Version euch zu Umbauten zwingt.
Teuer wird die falsche Wahl selten am ersten Tag. Sie wird es beim zweiten Dienst, wenn Protokollierung, Zeitlimits und Authentifizierung ein zweites Mal geschrieben werden müssen, weil sie an ein Framework gebunden sind statt an eine gemeinsame Zwischenschicht. Und sie wird es bei der Aktualisierung, wenn ein Versionssprung die Pfadsyntax ändert oder eine Bibliothek seit zwei Jahren keine Veröffentlichung mehr hatte und die nächste Anpassung bei euch liegt.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Die drei nebeneinander
Verglichen wird nicht der Durchsatz, denn dort nehmen sich die drei wenig. Verglichen wird, was ihr selbst bauen müsst, was ihr wiederverwenden könnt und wie ruhig die Schnittstelle bisher war.
Axum
das Framework aus dem Tokio-Umfeld, das auf hyper und tower aufsetzt und bewusst wenig eigene Mechanik mitbringt
Actix Web
der ausgereifte Vollausbau mit eigener Zwischenschicht und Makros für die Wegewahl
Rocket
der bequemste Einstieg, mit Formularen, Vorlagen und Konfiguration ab Werk
| Entscheidungsfrage | Axum | Actix Web | Rocket |
|---|---|---|---|
| Wie viel vom übrigen Ökosystem lässt sich unverändert übernehmen? | Stärke Zwischenschichten sind gewöhnliche tower-Layer. Protokollierung, Zeitlimits, Kompression und Ratenbegrenzung aus tower-http laufen unverändert auch vor einem gRPC-Dienst, der auf derselben Grundlage aufsetzt. | Kommt darauf an Die Zwischenschichten folgen einem eigenen Trait und bleiben damit an das Framework gebunden. Alles darunter, also Tokio und der Rest der asynchronen Welt, ist gemeinsam nutzbar. | Kommt darauf an Die Erweiterungen heißen Fairings und sind ein eigenes Konzept. Was ihr dort schreibt, bleibt in Rocket und wandert nicht in einen anderen Dienst mit. |
| Wie ruhig war die Schnittstelle in den letzten Jahren? | Kommt darauf an Die Sprünge von 0.6 auf 0.7 und weiter auf 0.8 brachten jeweils Anpassungen mit, zuletzt eine geänderte Schreibweise für Pfadparameter. Innerhalb der 0.8er-Reihe erscheinen die Aktualisierungen dagegen regelmäßig und unauffällig. | Stärke Die 4er-Reihe läuft seit Jahren und wird weiterhin gepflegt, zuletzt im August 2026. Aktualisierungen innerhalb der Reihe kosten in der Regel keine Codeänderung. | Schwäche Die aktuelle Fassung 0.5.1 stammt vom Mai 2024, seitdem gab es keine neue Veröffentlichung. Eine Schnittstelle, die sich nicht ändert, weil nichts erscheint, ist etwas anderes als eine gepflegte Schnittstelle. |
| Wie kommen Anfragedaten in die Handler? | Stärke Über Extraktoren als gewöhnliche Funktionsparameter, ohne Makro. Eine Regel muss man kennen: Der Extraktor, der den Rumpf der Anfrage verbraucht, steht zuletzt, sonst meldet der Compiler, dass der Handler nicht passt. | Stärke Ebenfalls über Extraktoren, dazu Attributmakros für die Wegewahl direkt an der Funktion. Zustand wird über einen typisierten Container hereingereicht, der zur Laufzeit aufgelöst wird. | Stärke Über Attributmakros mit getypten Pfaden und Abfrageparametern, dazu eine eingebaute Formularbehandlung. Von den dreien liest sich das beim ersten Blick am klarsten. |
| Wie schnell ist ein Team eingearbeitet? | Kommt darauf an Der erste Endpunkt steht in Minuten, das Verständnis für Extraktoren und die dahinterliegende Dienstabstraktion braucht länger. Passt ein Handler nicht, sind die Fehlermeldungen berüchtigt lang. | Kommt darauf an Dokumentation und Beispiele sind reichlich vorhanden. Die Hürde kommt erst beim Schreiben einer eigenen Zwischenschicht, denn dafür braucht es das framework-eigene Trait. | Stärke Der schnellste Weg zu einem lauffähigen Dienst, weil Formulare, Vorlagen, Konfiguration und ein Testclient mitkommen und nicht erst zusammengesucht werden. |
| Was ist ab Werk dabei, und was setzt ihr selbst zusammen? | Kommt darauf an Bewusst wenig. Protokollierung, Freigaben für fremde Ursprünge, Kompression, Zeitlimits und Auslieferung statischer Dateien holst du dir einzeln aus tower-http. Das ist mehr Aufbauarbeit und dafür austauschbar. | Stärke Ein großer Teil dessen, was ein Dienst braucht, liegt im Paket bei, von Sitzungen über statische Dateien bis zu Zwischenschichten für die üblichen Aufgaben. | Stärke Formulare, Vorlagen, getypte Verweise auf eigene Endpunkte und eine Konfiguration mit Profilen sind eingebaut. Genau das macht den schnellen Start aus. |
Wie viel vom übrigen Ökosystem lässt sich unverändert übernehmen?
Zwischenschichten sind gewöhnliche tower-Layer. Protokollierung, Zeitlimits, Kompression und Ratenbegrenzung aus tower-http laufen unverändert auch vor einem gRPC-Dienst, der auf derselben Grundlage aufsetzt.
Die Zwischenschichten folgen einem eigenen Trait und bleiben damit an das Framework gebunden. Alles darunter, also Tokio und der Rest der asynchronen Welt, ist gemeinsam nutzbar.
Die Erweiterungen heißen Fairings und sind ein eigenes Konzept. Was ihr dort schreibt, bleibt in Rocket und wandert nicht in einen anderen Dienst mit.
Wie ruhig war die Schnittstelle in den letzten Jahren?
Die Sprünge von 0.6 auf 0.7 und weiter auf 0.8 brachten jeweils Anpassungen mit, zuletzt eine geänderte Schreibweise für Pfadparameter. Innerhalb der 0.8er-Reihe erscheinen die Aktualisierungen dagegen regelmäßig und unauffällig.
Die 4er-Reihe läuft seit Jahren und wird weiterhin gepflegt, zuletzt im August 2026. Aktualisierungen innerhalb der Reihe kosten in der Regel keine Codeänderung.
Die aktuelle Fassung 0.5.1 stammt vom Mai 2024, seitdem gab es keine neue Veröffentlichung. Eine Schnittstelle, die sich nicht ändert, weil nichts erscheint, ist etwas anderes als eine gepflegte Schnittstelle.
Wie kommen Anfragedaten in die Handler?
Über Extraktoren als gewöhnliche Funktionsparameter, ohne Makro. Eine Regel muss man kennen: Der Extraktor, der den Rumpf der Anfrage verbraucht, steht zuletzt, sonst meldet der Compiler, dass der Handler nicht passt.
Ebenfalls über Extraktoren, dazu Attributmakros für die Wegewahl direkt an der Funktion. Zustand wird über einen typisierten Container hereingereicht, der zur Laufzeit aufgelöst wird.
Über Attributmakros mit getypten Pfaden und Abfrageparametern, dazu eine eingebaute Formularbehandlung. Von den dreien liest sich das beim ersten Blick am klarsten.
Wie schnell ist ein Team eingearbeitet?
Der erste Endpunkt steht in Minuten, das Verständnis für Extraktoren und die dahinterliegende Dienstabstraktion braucht länger. Passt ein Handler nicht, sind die Fehlermeldungen berüchtigt lang.
Dokumentation und Beispiele sind reichlich vorhanden. Die Hürde kommt erst beim Schreiben einer eigenen Zwischenschicht, denn dafür braucht es das framework-eigene Trait.
Der schnellste Weg zu einem lauffähigen Dienst, weil Formulare, Vorlagen, Konfiguration und ein Testclient mitkommen und nicht erst zusammengesucht werden.
Was ist ab Werk dabei, und was setzt ihr selbst zusammen?
Bewusst wenig. Protokollierung, Freigaben für fremde Ursprünge, Kompression, Zeitlimits und Auslieferung statischer Dateien holst du dir einzeln aus tower-http. Das ist mehr Aufbauarbeit und dafür austauschbar.
Ein großer Teil dessen, was ein Dienst braucht, liegt im Paket bei, von Sitzungen über statische Dateien bis zu Zwischenschichten für die üblichen Aufgaben.
Formulare, Vorlagen, getypte Verweise auf eigene Endpunkte und eine Konfiguration mit Profilen sind eingebaut. Genau das macht den schnellen Start aus.
Was passt wann
- Wenn ihr neben dem HTTP-Dienst weitere Netzdienste betreibt
- nimm Axum, weil dieselben Zwischenschichten davor laufen und ihr Protokollierung, Zeitlimits und Authentifizierung nur einmal schreibt.
- Wenn ein Dienst über Jahre laufen soll und niemand Umbauten wegen Versionssprüngen einplanen will
- spricht viel für Actix Web, dessen 4er-Reihe seit Jahren ohne Bruch fortgeschrieben wird.
- Wenn ihr Rust im Team gerade erst einführt und schnell etwas Vorzeigbares braucht
- geht Rocket am schnellsten von der Hand, klärt aber vorher, wer den nächsten Versionssprung übernimmt, falls er nicht von selbst kommt.
Sechs Fragen, bevor ihr einen Dienst in Rust neu baut
- 01 Läuft der Dienst dauerhaft, oder startet er nur für einen Auftrag?
- 02 Steckt die Antwortzeit in der Rechnung oder im Warten auf die Datenbank?
- 03 Kostet die Rechenzeit je Anfrage bei euch tatsächlich Geld?
- 04 Betreibt ihr weitere Dienste, die dieselben Zwischenschichten brauchen?
- 05 Wie eng ist das Speicherbudget je Instanz, wenn viele davon laufen?
- 06 Wer pflegt den Dienst in drei Jahren, und kennt diese Person Rust?
Danach triffst du die Wahl an Kriterien statt an Durchsatzzahlen
Die drei Frameworks lösen dieselbe Aufgabe und unterscheiden sich in dem, was drumherum entsteht. Wenn du weißt, welche Schicht euch die Wiederverwendung bringt und wie ruhig die Schnittstelle bisher war, ist die Entscheidung in einer halben Stunde begründet statt in einem Nachmittag verglichen.
Die vorgeschaltete Frage stellen
Du prüfst zuerst, ob der Dienst überhaupt zu den Fällen gehört, bei denen sich Rust rechnet: dauerhaft laufend, hohe Anfragezahl, teure Rechenschritte im Antwortpfad oder ein enges Speicherbudget je Instanz.
Zwischenschichten einmal schreiben
Du erkennst, warum eine an tower gebundene Schicht auch in einem gRPC-Dienst läuft und eine framework-eigene nicht, und richtest die Auswahl danach aus, wie viele Dienste ihr betreibt.
Die Versionsgeschichte lesen
Du siehst dir vor der Entscheidung an, wann zuletzt veröffentlicht wurde und was die letzten Sprünge an Umbauten gekostet haben, statt dich auf den Eindruck der Startseite zu verlassen.
Den Datenbankzugriff getrennt entscheiden
Du wählst zwischen sqlx, Diesel und SeaORM nach der Frage, ob euer Team SQL schreiben will, und weißt, wie die Prüfung der Abfragen ohne laufende Datenbank in der Pipeline funktioniert.
Beobachtbarkeit von Anfang an einbauen
Du setzt strukturierte Protokollierung mit Spannen über die Zwischenschicht auf, statt sie später in jeden Handler zu streuen.
Den Betrieb mitplanen
Du kennst den Gewinn bei Abbildgröße, Startzeit und Speicherverlauf und ebenso den Preis: Ein kalter Release-Build dauert Minuten und gehört in der Pipeline zwischengespeichert.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Für welche Dienste sich Rust rechnet, und für welche nicht
Die Fälle, in denen sich der Aufwand trägt, haben ein gemeinsames Muster: Der Dienst läuft dauerhaft, und die Rechenzeit je Anfrage ist ein Posten, den jemand bezahlt. Das gilt für Schnittstellen mit hoher Anfragezahl, für alles mit einer zugesagten Antwortzeit im oberen Perzentil, für teure Schritte im Antwortpfad wie Bildumwandlung, Kompression oder Signaturprüfung und für Aufbauten, bei denen viele Instanzen nebeneinander laufen und jede davon Arbeitsspeicher kostet. Der letzte Punkt wird oft übersehen und ist in Containerumgebungen der greifbarste, weil ein niedriger und vor allem gleichmäßiger Speicherverlauf direkt die Zahl der Instanzen je Knoten bestimmt.
Die Gegenprobe ist ebenso wichtig. Ein Dienst, dessen Antwortzeit von der Datenbank bestimmt wird, gewinnt durch die Sprache fast nichts. Wenn eine Anfrage vierzig Millisekunden auf Postgres wartet und zwei Millisekunden rechnet, verschiebt ein Wechsel die zwei Millisekunden und lässt die vierzig stehen. Dasselbe gilt für kurzlebige interne Werkzeuge, für Dienste mit wöchentlich wechselnden Anforderungen und überall dort, wo die Geschwindigkeit des Teams der Engpass ist und nicht die der Maschine.
Es gibt einen dritten Fall, der weder in die eine noch in die andere Schublade passt und trotzdem häufig ist: Ihr habt bereits Rust im Haus, etwa für einen Rechenkern oder eine Bibliothek, und der Dienst soll dieselbe Logik ohne zweite Implementierung anbieten. Dann entscheidet nicht der Durchsatz, sondern dass eine einzige Fassung gepflegt wird.
Was die drei tatsächlich unterscheidet
Der Unterschied, auf den es ankommt, liegt eine Ebene unter der Wegewahl. Axum bringt keine eigene Vorstellung von Zwischenschichten mit, sondern nutzt die des tower-Ökosystems: Ein Layer ist eine Funktion, die einen Dienst umschließt und einen Dienst zurückgibt. Das klingt abstrakt und hat eine sehr konkrete Folge. Die Zwischenschicht, die eure Anfragekennung setzt, Zeitlimits durchsetzt und Spannen für die Protokollierung öffnet, ist nicht an HTTP und nicht an Axum gebunden. Sie läuft auch vor einem gRPC-Dienst, der auf derselben Grundlage aufsetzt.
Actix Web und Rocket lösen dasselbe Problem mit eigenen Begriffen. Bei Actix Web ist eine Zwischenschicht ein Trait mit einer eigenen Signatur, und das eigene Schreiben davon ist der Punkt, an dem die meisten das erste Mal ins Stocken geraten. Bei Rocket heißen sie Fairings und hängen sich an feste Punkte im Anfragezyklus. Beides funktioniert, beides bleibt aber innerhalb des Frameworks. Wenn ihr einen einzigen Dienst betreibt, ist das kein Argument. Wenn ihr fünf betreibt, ist es das entscheidende.
Bei den Handlern selbst nehmen sich die drei wenig. Alle arbeiten mit Extraktoren, die aus der Anfrage getypte Werte ziehen, und in allen dreien ist der häufigste Anfängerfehler derselbe: Zwei Extraktoren wollen den Rumpf der Anfrage lesen, den es nur einmal gibt. Wie sich das äußert, ist verschieden. In Axum ist die Reihenfolge Teil der Regel, weil nur der letzte Parameter den Rumpf verbrauchen darf, und ein Verstoß ist ein Übersetzungsfehler mit einer berüchtigt langen Meldung. In Actix Web fällt es erst zur Laufzeit auf, wenn die Nutzlast bereits gelesen ist, und in Rocket lässt die Wegewahl von vornherein nur einen Datenparameter zu.
Datenbankzugriff: sqlx, Diesel oder SeaORM
sqlx ist keine Objektabbildung, sondern eine asynchrone Schicht für SQL, das ihr selbst schreibt. Der Reiz liegt in den Makros: Sie schicken die Abfrage beim Übersetzen an eine echte Datenbank, prüfen sie gegen das dortige Schema und leiten die Typen der Spalten daraus ab. Ein Tippfehler im Spaltennamen ist damit ein Übersetzungsfehler und kein Vorfall im Betrieb. Für die Pipeline gibt es den Offline-Modus: cargo sqlx prepare legt die geprüften Abfragen in einem Verzeichnis ab, das ihr mit eincheckt, sodass der Build ohne laufende Datenbank auskommt. Wer diesen Schritt vergisst, hat einen Build, der auf dem eigenen Rechner läuft und in der Pipeline scheitert.
Diesel geht den anderen Weg und beschreibt Abfragen in einer getypten Sprache innerhalb von Rust. Das Schema wird aus der Datenbank erzeugt und liegt als Rust-Code vor, die Migrationen bringt das Werkzeug mit, und die Prüfung findet vollständig im Typsystem statt, ohne dass beim Übersetzen eine Datenbank erreichbar sein muss. Der Preis ist die Lernkurve: Wer eine Abfrage im Kopf als SQL formuliert, muss sie erst übersetzen, und komplexe Abfragen führen zu Ausdrücken, die man beim Lesen zurückübersetzt.
SeaORM sitzt dazwischen und ist das, was Leute aus anderen Sprachen unter einer Objektabbildung verstehen: Entitäten, Beziehungen, ein Abfragebauer, asynchron und auf sqlx aufsetzend. Die Entscheidung lässt sich auf eine Frage eindampfen: Will euer Team SQL schreiben? Wenn ja, nehmt sqlx und behaltet die volle Kontrolle über die Abfrage. Wenn nicht, ist SeaORM der geringere Widerstand, und Diesel lohnt sich dort, wo die Prüfung ohne erreichbare Datenbank stattfinden muss.
Serialisierung, Konfiguration und Beobachtbarkeit
Bei der Serialisierung gibt es keine Entscheidung zu treffen: serde ist der gemeinsame Nenner des ganzen Ökosystems. Du setzt Derive-Attribute an deine Strukturen, und dieselbe Definition trägt danach durch JSON in der Schnittstelle, durch die Konfigurationsdatei und durch die Ablage in der Datenbank. Alle drei Frameworks bringen dafür Extraktoren und Antworttypen mit. Was serde ausdrücklich nicht macht, ist fachliche Prüfung: Dass ein Feld existiert und ein String ist, sagt nichts darüber, ob die enthaltene Kundennummer gültig ist. Diese Schicht gehört getrennt und in eure eigenen Typen.
Für Protokollierung hat sich tracing durchgesetzt, und der Unterschied zu einer klassischen Protokollbibliothek ist der Grund dafür. Statt einzelner Zeilen erzeugt tracing Spannen, also Zeitabschnitte mit Kontext, die sich verschachteln. Eine Anfragekennung, die ganz außen in der Zwischenschicht gesetzt wird, taucht damit in jeder Ausgabe darunter auf, ohne dass sie irgendwo durchgereicht werden muss. In Verbindung mit einer strukturierten Ausgabe landet das direkt auswertbar in eurer Protokollsammlung, und dieselben Spannen lassen sich als Ablaufverfolgung exportieren.
Die Konfiguration führt ihr über Umgebungsvariablen mit typisierter Auswertung beim Start. Der Punkt, der sich auszahlt: Ein Dienst, der beim Start eine ungültige Konfiguration bemerkt und mit einer klaren Meldung abbricht, ist deutlich angenehmer als einer, der drei Stunden später beim ersten Zugriff auf ein fehlendes Feld stolpert. In Rust ist das kein Aufwand, weil die Auswertung ohnehin über dieselben serde-Ableitungen läuft.
Was im Betrieb ankommt, und was es kostet
Der greifbarste Gewinn zeigt sich beim Ausliefern. Eine gegen musl statisch gebundene Binärdatei braucht kein Basisabbild mit Laufzeitumgebung, sodass das Container-Abbild im Wesentlichen aus der Binärdatei und den Wurzelzertifikaten besteht. Der Start dauert Millisekunden, weil weder eine virtuelle Maschine hochfährt noch eine Aufwärmphase durchlaufen wird, in der ein Übersetzer erst Tempo aufbaut. Bei rollierenden Neustarts und bei Aufbauten, die auf null herunterskalieren, ist das der Unterschied zwischen unauffällig und spürbar.
Der zweite Punkt ist der Speicherverlauf. Ohne Speicherbereinigung gibt es keine Kurve, die zwischen zwei Läufen anschwillt, und keinen Parameter, den jemand auf die Instanzgröße einstellen muss. Das Budget je Instanz lässt sich damit enger setzen, und die Zahl der Instanzen je Knoten steigt entsprechend. Genau diese Rechnung ist in den meisten Fällen die, mit der sich ein Wechsel gegenüber dem Budget begründen lässt, nicht die Zahl der Anfragen pro Sekunde.
Der Preis steht auf der anderen Seite und wird beim Planen gern unterschlagen: Übersetzungszeiten. Ein kalter Release-Build mit Linkzeit-Optimierung dauert Minuten, und in der Pipeline trifft euch das bei jedem Lauf. Die übliche Antwort ist eine Zwischenschicht im Abbild, die zuerst nur die Abhängigkeiten baut, sodass eine Änderung an eurem Code nicht den ganzen Baum neu übersetzt, dazu ein zwischengespeichertes Zielverzeichnis. Wer das nicht einrichtet, verliert bei jedem Pull Request mehr Zeit, als der Dienst im Betrieb einspart.
Dazu passende Kurse
Der erste Endpunkt steht in Minuten, ein Dienst mit Protokollierung, Zeitlimits und Datenbankschicht braucht länger, und dabei helfen Rust-Kurse für Netzwerkdienste .
Weil sich der Gewinn erst im Abbild, in der Startzeit und in der Pipeline zeigt, gehören DevOps-Kurse für Betrieb und Auslieferung in dieselbe Planung.
Wie sicher bist du beim Thema wirklich?
Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.
Wo du genau das übst
Sehr angenehmes, gut vorbereitetes Seminar, kann ich nur empfehlen.
Häufige Fragen
Ist Axum schneller als Actix Web?
Können wir Rocket noch guten Gewissens einsetzen?
Brauchen wir für einen Rust-Dienst eine eigene asynchrone Laufzeit?
Wie prüfen wir SQL-Abfragen in der Pipeline, wenn dort keine Datenbank läuft?
Deine Ansprechpartner
Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.
Yves Hoppe
Weiterbildung & Beratung
Hilft dir, aus dem Rust-Programm den passenden Kurs für deinen Stand zu finden.
Norbert Jansen
Beratung & Inhouse
Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.
Ein Dienst, der auch beim zweiten und dritten noch trägt
Wie Extraktoren, Zwischenschichten und Datenbankzugriff zusammenspielen, bis daraus ein wartbarer Dienst wird, baust du bei cmt einmal vollständig auf.