1 Punkte von GN⁺ 5 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • zeroserve, ein kleiner und schneller HTTPS-Server, nimmt ein Website-Tarball entgegen, liefert es über HTTP/2 und TLS 1.3 aus und führt bei jeder Anfrage die im Tarball enthaltenen eBPF-Programme als Middleware-Sandbox im User Space aus
  • Ohne Konfigurationsdatei entscheidet ein eBPF-Programm pro Anfrage über Routing, Header, Authentifizierung, Rate Limiting und Proxying und vereint damit die deklarative Konfiguration von nginx und Caddy mit einer separaten Skripting-Schicht in einem Modell
  • Die Site wird als einzelne tar-Datei indiziert und nicht auf die Festplatte entpackt; durch Ersetzen des Tarballs und SIGHUP werden Site, Skripte und TLS-Daten atomar ohne Verbindungsverlust ausgetauscht
  • In HTTPS-Benchmarks auf einem einzelnen Core erreichte zeroserve 36.681 req/s für kleine statische Dateien, 46.945 req/s für dynamisches JSON via eBPF mit 10 ms und 26.486 req/s als kleiner Proxy, bei 100KB-Proxying liegt jedoch nginx mit 5.882 req/s vorn
  • Als Alternative zu nginx und Caddy kombiniert zeroserve Deployment als einzelnes Tarball, programmatische Konfiguration, eBPF im User Space und modernes TLS, für große Proxy-Antworten ist nginx jedoch besser geeignet

Überblick

  • zeroserve ist ein kleiner, schneller konfigurationsloser HTTPS-Server, der ein einzelnes Website-Tarball über HTTP/2 und TLS 1.3 ausliefert
  • Im Tarball enthaltene eBPF-Programme laufen bei jeder Anfrage als Middleware-Sandbox im User Space und können Request-Rewriting, Authentifizierung, Rate Limiting und Backend-Reverse-Proxying übernehmen
  • Das Ziel ist ein Server, der auf einem einzelnen Core bei den meisten Workloads für kleine und große statische Dateien, skriptbasierte Middleware und Proxying kleiner Antworten höhere Leistung als nginx zeigt
  • eBPF-Skripte werden per JIT in nativen Code kompiliert, im User Space sandboxed und sollen so geringe Kosten haben, dass sie pro Anfrage ausgeführt werden können
  • Netzwerk- und Festplatten-I/O werden über die monoio-Runtime mit io_uring eingereicht
  • Unterstützung für TLS 1.3, HTTP/2, Encrypted Client Hello, SNI-Zertifikatsauswahl und JA4-Fingerprinting
  • Die komplette Site und alle TLS-Daten kommen aus einem einzigen Tarball und lassen sich per SIGHUP hot reloaden

Konfigurationsmodell: Das Programm ist die Konfiguration

  • zeroserve zielt auf eine Alternative zu nginx und Caddy, und die zentrale Designentscheidung ist das Konfigurationsmodell
  • nginx und Caddy bieten deklarative Konfigurationssprachen mit location-Blöcken, rewrite-Regeln, map-Direktiven und try_files; wenn diese an Grenzen stoßen, wird daneben eine optionale Skripting-Runtime wie Lua oder ein Caddy-Plugin ergänzt
  • In dieser Struktur teilt sich das Verhalten in eine Schicht aus Direktiven mit eigenem Kontrollfluss und eine Skript-Schicht, die an bestimmten Punkten im Request-Lebenszyklus läuft
  • zeroserve hat keine Konfigurationsdatei; ein einziges eBPF-Programm sieht alle Anfragen und entscheidet über Routing, Header, Authentifizierung, Rate Limiting und Proxying

Ein einzelnes Tarball direkt ausliefern

  • Die komplette Site ist eine einzelne tar-Datei; beim Laden erstellt zeroserve eine path -> byte-range-Map und liest Byte-Bereiche direkt aus dem Tarball, um Dateien auszuliefern
  • Da keine Dateien auf die Festplatte entpackt werden, existiert die Site nur innerhalb dieser einen Datei, und es gibt kein Document Root, das durch fehlerhafte location-Regeln offengelegt werden könnte
  • Das Deployment erfolgt durch atomaren Austausch einer einzelnen Datei; für ein neues Release wird das Tarball ersetzt und anschließend SIGHUP gesendet
  • Das Paketieren eines Verzeichnisses und der Startbefehl haben folgendes Format
zeroserve --pack ./public > site.tar  
zeroserve --addr 0.0.0.0:8080 site.tar  
Anzeige
  • Der Hot-Reload-Befehl hat folgendes Format
killall -SIGHUP zeroserve  
  • Beim Reload werden Site, Skripte und TLS-Daten innerhalb desselben Prozesses atomar ausgetauscht, ohne Verbindungen zu verlieren
  • Jede Instanz ist eine Single-Thread-Event-Loop; das ist pro Prozess eine Begrenzung, wird aber als passende Form präsentiert, wenn die Skalierungseinheit „mehr Prozesse“ ist

eBPF-Skripting im User Space

  • Alle .c-Dateien unter .zeroserve/scripts/ werden beim Paketieren mit clang und llc zu eBPF-Objekten kompiliert und bei jeder Anfrage ausgeführt
  • eBPF läuft im User Space in der async-ebpf-Runtime innerhalb eines normalen unprivilegierten Prozesses, ohne Kernel-BPF-Subsystem oder CAP_BPF
  • async-ebpf enthält uBPF und kompiliert den Bytecode per JIT in nativen x86-64-Maschinencode
  • Ein Pointer Cage maskiert alle Speicherzugriffe des JIT-kompilierten Codes auf eine programspezifische Arena und hält fehlerhafte Zugriffe innerhalb des Skriptspeichers eingeschlossen
  • Skripte laufen direkt in der Single-Event-Loop von zeroserve; damit langsame Skripte andere Verbindungen nicht anhalten, kann ein Timer den laufenden JIT-kompilierten nativen Code unterbrechen und die Kontrolle an die Event-Loop zurückgeben
  • Das Programmiermodell ist eine Skriptkette, die in nach Dateinamen sortierter Reihenfolge läuft; die Skripte teilen sich eine anfragebezogene Metadata-Map
  • Wenn ein Skript zs_respond oder zs_reverse_proxy aufruft, wird die Kette kurzgeschlossen
  • Schlüssel unter zs.response.header.* werden zu Headern jeder Antwort; andere Schlüssel werden in einem kleinen Template-Durchlauf genutzt, der Platzhalter wie <zs-meta>visitor</zs-meta> in HTML-Dateien beim Ausgeben ersetzt
  • Die Helper-Oberfläche unterstützt das Lesen von Request-Methode, Pfad, Query, Headern und Peer-Adresse sowie URI-Rewriting und das Setzen oder Entfernen von Headern
  • Krypto- und Encoding-Helper bieten SHA-256, HMAC-SHA256, base64, hex und getrandom
  • JSON-Helper unterstützen das Parsen des Request-Bodys, das Erzeugen und Modifizieren eines Dokumentbaums sowie Antworten mit zs_json_respond
  • Rate Limiting unterstützt Token Buckets auf Basis beliebiger Schlüssel wie Peer-IP oder API-Key; der Zustand bleibt auch nach einem Hot Reload erhalten
  • AWS-SigV4-Helper unterstützen signierte Authorization-Header und presigned URLs für S3 und andere AWS-Dienste
  • OIDC-Login bietet einen Relying-Party-Flow auf Basis von Authorization Code + PKCE; die komplette Login-Session wird in einem versiegelten XChaCha20-Poly1305-Cookie gespeichert, sodass der Server zustandslos bleibt und statische Sites hinter „Mit Google anmelden“ gestellt werden können
  • Dynamische Endpunkte funktionieren so, dass Skripte auf bestimmten Pfaden direkt antworten; im Beispiel liefert eine Anfrage an /health den Header application/json und den Body {"status":"ok"} zurück
  • Jedes Skript läuft standardmäßig unter einem Speicherlimit von 256 KB; die Runtime time-sliced lang laufende Skripte im Executor und drosselt ausreißende Skripte
  • Skripte können sich mit zs_call gegenseitig aufrufen, die Aufruftiefe ist begrenzt
  • Ein Skript in einer Endlosschleife verzögert nur seine eigene Anfrage; ein präemptiver Timer unterbricht es, damit der Server weitere Anfragen weiterbearbeiten kann
  • Die TLS-Schicht ist auf TLS 1.3 beschränkt und terminiert mit BoringSSL
  • Encrypted Client Hello verhindert, dass das echte SNI im Klartext erscheint, und bietet verzeichnisbasierte SNI-Zertifikatsauswahl sowie JA4-Client-Fingerprinting, das Skripten zugänglich ist
  • Im transparenten ECH-Relay-Modus werden nicht entschlüsselbare Handshakes Byte für Byte an den eigentlichen Upstream weitergereicht, sodass geschützte Namen hinter öffentlichen Namen versteckt werden können

Leistung

  • Benchmark-Bedingungen

    • Verglichen wurden zeroserve, nginx 1.26 und Caddy 2.11 bei HTTPS-Auslieferung desselben Inhalts mit demselben selbstsignierten Zertifikat auf einem 8-Core Ryzen 7 3700X
    • Da eine zeroserve-Instanz konzeptionell Single-Threaded ist, dient Leistung pro Core als Vergleichsmaßstab
    • Alle Server wurden mit taskset an eine CPU gebunden; nginx mit worker_processes 1, Caddy mit GOMAXPROCS=1, zeroserve mit seiner bestehenden Single-Thread-Struktur
    • Last wurde auf anderen Cores mit wrk -t4 -c100 erzeugt, verwendet wurde der Median aus drei Läufen à 10 Sekunden
    • wrk nutzt HTTP/1.1, daher messen die Werte HTTP/1.1 über TLS 1.3 und damit die Steady-State-Kosten bereits geöffneter HTTPS-Verbindungen, bei denen lange Keep-Alive-Verbindungen die Handshake-Kosten verteilen
  • Kleine statische Datei 174B

    Server req/s p99
    zeroserve 36.681 5.4 ms
    nginx 31.226 7.8 ms
    Caddy 12.830 22 ms
    • zeroserve lieferte kleine Dateien auf einem einzelnen Core rund 17 % schneller als nginx aus und hatte auch eine niedrigere Tail-Latenz
    • Der typische Basisfall statischer Sites wie HTML-Seiten, kleines JSON und CSS ist das Ziel der Optimierung von zeroserve
    Anzeige
  • Große statische Datei 100KB

    Server req/s Durchsatz p99
    zeroserve 8.000 782 MB/s 22 ms
    nginx 7.600 773 MB/s 28 ms
    Caddy 6.084 590 MB/s 44 ms
    • Die Ergebnisse der drei Server lagen nah beieinander, zeroserve lag auf einem einzelnen Core mit rund 780 MB/s leicht vorn
    • Die Stärke von nginx bei großen Dateien, sendfile(), wird unter TLS nicht genutzt; die Bytes müssen im User Space verschlüsselt werden, daher hängen alle drei Server an Verschlüsselung und Schreibschleife
    • Bei deaktiviertem Kernel TLS auf allen drei Servern war der io_uring-Lese-/Schreibpfad von zeroserve leicht schneller

eBPF vs Lua

  • Als Vergleich für Skripting dient nginx + LuaJIT ngx_http_lua_module, ein verbreiteter Weg, schnellen Code innerhalb eines Webservers auszuführen
  • zeroserve setzt standardmäßig einen präemptiven Skript-Timer auf 2 ms; ein feineres Intervall drosselt problematische Skripte schneller, kostet aber auch bei normalen Skripten Leistung
  • Mit den standardmäßigen 2 ms liegt eBPF bei vollständig dynamischen Antworten mit rund 32k req/s unter nginx Lua mit 41k req/s
  • Wird --preempt-timer-interval-ms auf 10 erhöht, erholt sich der Skripting-Durchsatz um rund 40 % und das Ergebnis dreht sich um
  • Middleware zur Header-Injektion pro Anfrage

    Engine req/s p99
    zeroserve eBPF 10ms 43.709 5.1 ms
    zeroserve eBPF 2ms Standard 31.334 6.7 ms
    nginx Lua header_filter 28.653 8.4 ms
    • Im Middleware-Fall, in dem das Skript läuft, aber weiterhin statische Dateien ausgeliefert werden, liegt eBPF mit 10 ms etwa 50 % über nginx Lua und hat zugleich niedrigere Tail-Latenz
  • Vollständig dynamische JSON-Antwort

    Engine req/s p99
    zeroserve eBPF 10ms 46.945 4.5 ms
    nginx Lua content_by_lua 41.231 6.4 ms
    zeroserve eBPF 2ms Standard 32.393 6.7 ms
    • Getuntes eBPF mit 10-ms-Intervall erreicht auch bei vollständig synthetischen Antworten höheren Durchsatz als nginx mit content_by_lua
    • Beide Engines kompilieren in nativen Code; LuaJIT ist ein Tracing-JIT, async-ebpf JIT-kompiliert eBPF über uBPF
    • Unter Bedingungen, in denen TLS-Verschlüsselung die gemeinsamen Request-Kosten dominiert, liegt der getunte eBPF-Pfad beim Durchsatz vorn
    • Mit dem 2-ms-Standardwert behält eBPF zwar den Vorteil bei Middleware, verliert aber die Führung bei synthetischen Antworten; für produktive Skripte werden daher 10 ms empfohlen
Anzeige

Einsatz als Reverse Proxy

  • zeroserve proxyt zu einem Backend, indem im Skript zs_reverse_proxy("http://127.0.0.1:9000";) aufgerufen wird
  • Der Upstream-Connection-Pool unterstützt pro Backend bis zu 128 Verbindungen und 30 Sekunden Wiederverwendung im Idle-Zustand
  • Für einen fairen Vergleich nutzt nginx explizit keepalive 128, proxy_http_version 1.1 und einen geleerten Connection-Header, da es standardmäßig Upstream-Verbindungen pro Anfrage schließt
  • Caddy verwendet wie im Standardverhalten Verbindungswiederverwendung
  • Jeder Proxy terminierte TLS auf einem einzelnen Core und leitete an ein gemeinsames Plaintext-Backend weiter; das Backend lief auf einem separaten 2-Core-Server mit eigenen 100k req/s, sodass nur der Proxy-Overhead gemessen wurde
  • Proxy für kleine 174B-Antworten

    Proxy req/s p50 p99
    zeroserve 26.486 3.3 ms 8 ms
    nginx 21.761 4.2 ms 10.5 ms
    Caddy 7.683 10.3 ms 33 ms
    • Der gepoolte io_uring-Proxy von zeroserve lag rund 22 % vor nginx und erreichte etwa die 3,4-fache Leistung von Caddy
    • Bei typischen Proxy-Workloads wie API-Calls, kleinem JSON und HTML aus App-Servern übernimmt zeroserve TLS-Terminierung und Backend-Weiterleitung schneller
  • Proxy für 100KB-Antworten

    Proxy req/s Durchsatz
    nginx 5.882 585 MB/s
    Caddy 4.285 406 MB/s
    zeroserve 3.631 359 MB/s
    • Werden die Proxy-Bodies größer, bewegt das Buffering von nginx die Bytes effizienter und bringt nginx an die Spitze, Caddy liegt in der Mitte und zeroserve dahinter
    • Bei großen Proxy-Antworten ist nginx das bessere Werkzeug, bei vielen kleinen Antworten ist zeroserve schneller

Speicher

  • Eine einzelne untätige zeroserve-Instanz nutzt etwa 15 MB PSS, mehr als nginx mit rund 6 MB, aber weniger als Caddy mit rund 60 MB
  • Wichtig ist, dass die Ausführungseinheit der gesamte Prozess ist; wenn pro Core eine Kopie läuft, wird dasselbe Binary gemappt und die Code-Seiten werden geteilt
  • Zusätzliche Prozesse erhöhen den Speicherverbrauch abgesehen von ihrem eigenen Working Set nur geringfügig

Veröffentlichung

  • zeroserve ist ein als Open Source auf GitHub veröffentlichtes Projekt

1 Kommentare

 
GN⁺ 5 시간 전
Hacker-News-Kommentare
  • Mit dem Verschwinden der TechEmpower-Webserver-Benchmarks scheint es für solche neuen Projekte weniger Gelegenheiten zu geben, sich selbst zu beweisen.
    Edit: Offenbar bin ich nicht mehr auf dem Laufenden, und das hier scheint aktuell im Kommen zu sein: https://www.http-arena.com/leaderboard/. Viel Glück.

    • Ich weiß nicht, was mit „verschwunden“ gemeint ist. Es ist noch da: https://www.techempower.com/benchmarks/#section=data-r23, und der letzte Benchmark stammt von Februar 2025.
      Allerdings liefen die ohnehin nie besonders häufig, und wenn man sich die Historie der Runden ansieht, wurden sie seltener als einmal pro Jahr ausgeführt.
    • Die LLM-UI/UX ist wirklich miserabel. Wenn man nur ein oder zwei Wochenendtage darauf verwenden würde, ließe sich die Nutzererfahrung deutlich verbessern; ich verstehe nicht, warum das nicht passiert.
  • Es ist schön zu sehen, dass solche Versuche sich dank LLMs relativ günstig und schnell erkunden lassen.
    Mein Eindruck hier ist allerdings auch, dass nginx selbst ziemlich beeindruckend ist. Ein weiterer auffälliger Punkt war die Beschreibung, dass dieses Projekt eine Alternative zu nginx und Caddy sei und auf die Art der Konfiguration setzt.
    nginx und Caddy bieten deklarative Konfigurationssprachen, und wenn man an deren Grenzen stößt, hängt man daneben eine Skript-Laufzeit wie Lua oder Caddy-Plugins an, sodass das Verhalten in zwei Ebenen aufgeteilt ist.
    Ich glaube aber, dass diese Wette falsch ist. Menschen bevorzugen schon seit Langem Konfiguration statt Code, und in vielen Fällen reichen die eingebauten Funktionen aus, sodass man keinen C-Code schreiben muss.

    • So sicher wäre ich mir da nicht.
      Alle Konfigurationsdateiformate scheinen zunächst einfach zu beginnen. Selbst YAML war anfangs im Kern ziemlich vernünftig, bis die Leute mit Anchors und Aliases mehr Komplexität wollten.
      Sogar GitLab hat ein eigenes Format mit einer Art Bedingungen und Variablen, das nur an bestimmten Stellen funktioniert und fast schon ein Hack ist. Apache ist mit seinem XML-basierten Konfigurationsformat einen ähnlichen Weg gegangen.
      Am Ende entstehen unzählige maßgeschneiderte Programmiersprachen zur Verwaltung von Konfigurationen. In Unternehmensumgebungen bearbeitet man sie nicht einmal direkt, sondern schreibt Ansible-Workflows als Skripte für Fernoperationen.
      Hätte man stattdessen einfach einen Interpreter wie Lua oder Python in den Server eingebettet und die Konfigurationsverwaltung darüber laufen lassen, hätte man diesen ganzen Prozess überspringen können, und es wäre einfacher gewesen, als benutzerdefinierte Konfigurationsdateien per Programm zu manipulieren.
      Man kann natürlich argumentieren, dass maßgeschneiderte Ansätze für bestimmte Zwecke besser optimiert seien als allgemeine Sprachen, aber dieses Argument passt von vornherein nur in den engen Rahmen von Spielzeugbeispielen, bei denen man diese Konstruktion vermutlich nie gebraucht hätte.
      Erinnern Sie sich an Windows-INI-Dateien? Das waren noch gute Zeiten, als Code Code und Daten Daten waren.
    • Ich könnte mir vorstellen, dass innerhalb der nächsten 96 Stunden jemand mit einem LLM ein Werkzeug zum Umwandeln und Verpacken von nginx- oder Caddy-Konfigurationsdateien in von zeroserve nutzbaren Code baut.
      Noch einfacher wäre es, einfach alle Ingress-Manifeste in einem Kubernetes-Cluster zu lesen und pack neu zu erzeugen.
      Der Punkt ist, dass die Schnittstelle zwischen Werkzeugen und Konfiguration ebenfalls nur eine weitere API ist und Systembetreiber den Systemzustand ohnehin bereits mit höherwertigen Konstrukten beschreiben, während die konkreten Bytes der Konfiguration nur das Ergebnis davon sind.
    • Wie wäre es damit, Komplexität zu abstrahieren und per Makros eine Art „Konfigurationsdatei“-artige Zusammensetzung zu erreichen?
    • Es könnte sich lohnen zu beobachten, ob sich diese Präferenz ändert, während AI immer stärker menschliche Sprache → maschinelle Wirkung ermöglicht.
      Aus Sicht von AI könnte diese Art der Interaktion leichter handhabbar sein. AI kann ohnehin beide Seiten verarbeiten, daher könnte es lange dauern, bis sich so ein Wandel als eindeutig gute Idee etabliert.
    • Ich verstehe nicht, warum man das unbedingt LLMs zuschreiben will. Nur weil man sich beim Schreiben des Artikels von einem LLM helfen ließ, heißt das nicht, dass das LLM auch die Experimente durchgeführt hat.
  • Die Idee gefällt mir.
    Ich würde mich aber wohler fühlen, wenn man im eBPF-Verzeichnis statt .c-Dateien .rs-Dateien ablegen könnte. Es ist ja ohnehin bereits ein Rust-Projekt.
    Und irgendwie hatte ich einen kernelbeschleunigten Webserver erwartet. Wenn sich das mit eBPF sicher umsetzen ließe, wäre das wirklich beeindruckend.
    Und dann nur ein einzelner Thread? Unter Linux ist es fast trivial, zu forken und die Queue eingehender Verbindungen gemeinsam zu nutzen, und in Rust wäre das wohl in ein paar Zeilen erledigt. Mit SO_REUSEPORT kümmert sich der Kernel um den Rest.
    Wenn man übrigens io_uring pushen will, sollte man meiner Meinung nach auch kTLS pushen. Wenn sich SSL-Verarbeitung im Userspace nach dem Handshake vermeiden lässt, vereinfacht das das Design erheblich.

    • Danke. fork + SO_REUSEPORT ist geplant.
      Bisher habe ich für solche Zwecke nftables verwendet, daher brauchte ich das nicht direkt.
  • Sehr cool. Ich frage mich, ob man das mit anderen BPF-Programmtypen kombinieren könnte, etwa einem XDP-Programm oder einem Programm, das an Socket-Maps hängt, um L7-HTTP-Funktionalität weiter nach unten in den Stack zu integrieren.

  • Die Idee ist gut, aber ich bin nicht sicher, ob der Fokus auf statischen Dateien liegen sollte. Heutzutage setzt man dafür nur noch selten eigens einen Server auf.

    • Letzte Woche habe ich Ghost in statische Inhalte umgewandelt und genau das gemacht, und ich dachte halbwegs, dass ein einzelnes selbstenthaltendes Binary vielleicht schneller wäre.
      Deshalb fühlt es sich an, als wäre das hier für mich gemacht, auch wenn ich zugeben muss, dass ich kein typischer Nutzer bin.
    • Hängt von der Domäne ab. In vielen wissenschaftlichen Bereichen werden große Datensätze effizient in statischen Dateiformaten ausgeliefert. Beispiele wären https://zarr.dev/ oder https://parquet.apache.org/.
  • Sieht gut aus und die Features sind auch okay. Aber irgendwie wirkt es zu künstlich, sodass ich nicht sofort überzeugt bin.
    Man weiß nicht, ob die Metriken fake sind, ob die Convenience-Funktionen wirklich funktionieren oder ob überhaupt eine ordentliche Härtung stattgefunden hat.
    Dass es mit Vibe Coding gebaut wurde und sogar das README automatisch erzeugt wurde, kann ich noch akzeptieren. Aber wenn sogar der Ankündigungs-Blogpost von AI geschrieben ist, habe ich keinerlei Grundlage zu beurteilen, ob das Verständnis von Softwarequalität mit meinem übereinstimmt.
    Seltsame Welt. Wenn das vor ein paar Jahren ohne AI-Hinweis veröffentlicht worden wäre, hätte ich es wohl ohne Misstrauen akzeptiert. Heute sehe ich ein schickes README und plausibel wirkende Kommandozeilenparameter und vermute sofort, dass das README halluziniert hat und es die Optionen in Wirklichkeit vielleicht gar nicht gibt.

    • Ich bin der Autor. Einige Kernteile dieses Projekts, zum Beispiel async-ebpf, wurden lange vor dem Auftauchen solcher Coding-Agenten geschrieben.
      Beim Bau von zeroserve nutze ich zwar viel AI-Unterstützung, aber ich prüfe die AI-Ausgaben selbst und trage auch die Verantwortung dafür.
    • Wenn man sich die Benchmarks ansieht: Bei einer kleinen statischen Datei mit 174 B erreicht zeroserve 36.681 req/s und p99 5,4 ms, nginx 31.226 req/s und p99 7,8 ms, Caddy 12.830 req/s und p99 22 ms.
      zeroserve liefert kleine Dateien auf einem einzelnen Core etwa 17 % schneller aus als nginx und hat auch eine engere Tail-Latenz. HTML-Seiten, kleine JSON-Dateien und CSS sind Fälle, in denen zeroserve passt.
      Bei einer großen statischen Datei von 100 KB erreicht zeroserve 8.000 req/s, 782 MB/s und p99 22 ms, nginx 7.600 req/s, 773 MB/s und p99 28 ms, Caddy 6.084 req/s, 590 MB/s und p99 44 ms.
      Trotzdem würde ich statt so eines jungen Projekts lieber ein geprüftes, im Praxiseinsatz bewährtes und gehärtetes altes Projekt wählen. Die Verbesserung ist nicht groß genug, um das Risiko zu rechtfertigen.
    • Wirklich eine bedauerliche Situation. Kürzlich gab es das Projekt ffmpeg-wasm, und als ich es getestet habe, funktionierte es auch. Aber es war Vibe-Coding-AI, und ich kann AI nicht ertragen. Selbst wenn es funktioniert, bleibt das so.
      Ich habe mich entschieden, möglichst in der alten Welt zu bleiben. Kluge Menschen veröffentlichen Software, und kluge Menschen warten sie. Dafür brauchen sie keine AI. Das ist meine Nische.
      Vielleicht verschwinden wir, aber selbst dann ist mir diese Seite lieber. Wobei das voraussetzt, dass diese klugen Menschen auch Dokumentation schreiben. Es gibt viele kluge Leute, die Dokumentation hassen.
      Ich habe schon vor langer Zeit entschieden, dass Software ohne Dokumentation meine Zeit nicht wert ist, egal wie großartig sie sein mag. Das gilt vor allem für Anwendungen. Linux-Dokumentation habe ich fast nie gelesen, aber andere sagen wohl, sie sei nicht ganz schlecht, also wer weiß.
  • Ein interessantes neues Konzept, und es gefällt mir.
    Die eigentliche Frage ist Engagement der Entwickler und Community. Die Leute hinter Caddy und Nginx unterstützen ihre Produkte schon lange kontinuierlich, und auch dieses Projekt wird viel Fokus und Aufmerksamkeit brauchen.

  • Warum ein tarball?

    • Es ist ein simples Format, bei dem sich Ressourcen leicht per Byte-Range ansprechen lassen, jeder hat Werkzeuge dafür, und vor allem ist es nicht komprimiert.
    • Laut dem ersten Absatz im Abschnitt „One tarball, served in place“ besteht die gesamte Website aus einer einzigen tar-Datei, und zeroserve indiziert diese beim Laden, erstellt aus den Pfaden eine Byte-Range-Map und liefert Dateien aus, indem es Byte-Range-Reads direkt auf dem tarball selbst ausführt.
      Auf die Platte wird nichts entpackt. Da die Site vollständig in dieser einen Datei steckt, gibt es kein Document Root, das durch fehlerhafte location-Regeln offengelegt werden könnte, und das Deployment wird zu einem einzigen atomaren Dateiaustausch.
      Allerdings könnte auch diese Erklärung eine typische LLM-Rechtfertigung sein. Im Text sind Formulierungen wie „the right shape“ oder „the surface is broad“ verstreut.