Skip to content
Zum Inhalt springen

Java kann nicht auf null skalieren? Diese Quarkus-App sieht das anders.

Eine Solo-Session: ein Reisebudget-Tracker für die Familie, gebaut mit Quarkus, Qute und Firestore, deployt auf Google Cloud Run und zweimal im Billing-Dashboard bei 0 € bestätigt. Zero Cost hat gehalten, und das Google-Login machte mehr Ärger als der App-Code.

Veröffentlicht: 10. September 2026Lesezeit: 6 Min. Lesezeit
Java kann nicht auf null skalieren? Diese Quarkus-App sieht das anders.

YouTube

Java kann nicht auf null skalieren? Diese Quarkus-App sieht das anders.

YouTube-Video laden

Dieses Video wird von YouTube eingebettet und erst nach Ihrer Einwilligung geladen. Beim Laden können personenbezogene Daten an YouTube oder Google übermittelt und Cookies gesetzt werden.

Auf YouTube öffnen

Mehr dazu in der Datenschutzerklärung.

Projektquelle

Arbeits-Repository

Hier findest du Prompts, Instructions und Beispiele aus dem gezeigten Modernisierungs-Workflow.

Repository öffnen
Timestamps der Session

Ein Familienurlaub in England, eine Woche vor dieser Session, war der Auslöser. Meine Kinder wollten Sachen kaufen und hatten keine Ahnung, wie viel Budget sie noch hatten. Ich habe es mit einer To-do-App mitgeschrieben, das funktionierte, war aber chaotisch. Ich bin auch geizig, was meine eigenen Nebenprojekte angeht: Ich zahle nichts dafür, ein Projekt nur für mich selbst zu hosten, will es aber trotzdem von überall erreichbar haben, für Familie und Freunde, ohne Kosten.

Diese Session hatte deshalb drei Regeln. Bauen mit einem KI-Coding-Tool, nicht von Hand. Erst lokal zum Laufen bringen. Und das komplette Cloud-Setup, Projekt, OAuth-Client, Cloud-Run-Deployment, ebenfalls über die KI, nicht über die GCP-Konsole oder auswendig gelernte gcloud-Befehle.

So sieht das Ganze im Zusammenspiel aus:

100%
BrowserFamilienmitglied auf dem HandyCloud RunQuarkus-App, min-instances = 0FirestoreFamilien-, Budget-, AusgabendatenGoogle OAuthLogin, JWKS lokal zwischengespeichert und geprüftGitHub ActionsMaven-Build, Java 21, Deploy bei Push auf mainHTTP-Request / Qute-HTMLliest + schreibt (gRPC)OIDC-Login-RedirectDeploy + OAuth-Secrets als Env-Vars

Der Stack, gewählt wegen der Kosten, nicht aus Gewohnheit

Jede Entscheidung hier hielt die laufenden Kosten bei null. Der Build blieb beim normalen JVM-Weg von Quarkus, ohne Native Image: Native Builds vertragen sich schlecht mit Reflection, und genau darauf setzen die Google-Cloud-Client-Bibliotheken, die dieses Projekt braucht. Immer wieder versuche ich, ein Native Image zu bauen, und es funktioniert nie richtig. Mein üblicher Quarkus-Cold-Start liegt sonst unter zwei Sekunden, das war also das Ziel auf der JVM.

Bei der UI hat sich Qute gegen Vaadin durchgesetzt. Ich bevorzuge Vaadin von Hand, aber in Kombination mit KI-Tools hat es mir schon Ärger gemacht: Lizenzprobleme, fehlende Annotationen, die die KI nicht erkannt hat. Mit KI kann ich es im Moment nicht empfehlen. Qute ist nah genug an reinem HTML, dass die KI es zuverlässig generiert.

Firestore statt einer SQL-Datenbank ist die Entscheidung, die "keine Kosten im Leerlauf" wahr macht: Eine SQL-Instanz kostet, solange sie läuft, egal ob genutzt oder nicht, Firestore nicht. Das Google-Login setzt darauf auf, über Quarkus' eigene quarkus-oidc-Extension mit provider=google: keine zusätzliche Bibliothek, keine selbstgeschriebene Credential-Verwaltung.

Nichts davon war vorher geprobt. Ich hatte Grok 4.6 vor diesem Stream noch nie benutzt, das Modell, mit dem GitHub Copilot im Autopilot-Modus lief, und Google Cloud hatte ich vorher auch noch nie ernsthaft genutzt.

100%
Sprach- / Chat-PromptKI-AgentCopilot, dann Claude CodeCloud-Run-Deploymentmin-instances = 0Firestore-Datenkeine LeerlaufkostenGitHub Actions CI/CDBuild + Deploy bei jedem PushGoogle-OAuth-Konsolevon Hand angelegt, Bild zweimal stummgeschaltetals GitHub-SecretJeder durchgezogene Pfeil lief über einen Prompt. Der gestrichelte nicht, und genau dort begann das Debugging.

Die App stand schnell. Das Login nicht.

Der erste lokale Start scheiterte an Firestore-Credential-Fehlern: keine Application Default Credentials lokal. Die Lösung war einfach: lokale Entwicklung auf einen In-Memory-Store umstellen, kein Docker, keine echten Credentials nötig. Auch das Datenmodell brauchte einen Fix. Budget und Ausgaben waren über die ganze Familie geteilt statt pro Person, was überhaupt keinen Sinn ergab, sobald ich es ausprobiert habe. Jedes Familienmitglied bekam sein eigenes Budget, eigene Ausgaben, optionales Einkommen, modelliert als unveränderliche Java-Records, nah an Event Sourcing.

Familien-Dashboard mit Budgets, Ausgaben, Einkommen und verbleibenden Summen für zwei Familienmitglieder, Anna und PeterDas Familien-Dashboard: eine gemeinsame Reise, ein privates Budget pro Person.
Familien-Dashboard mit Budgets, Ausgaben, Einkommen und verbleibenden Summen für zwei Familienmitglieder, Anna und Peter
Persönliches Budget für Anna mit Budget, Ausgaben, Einkommen und verbleibendem Betrag sowie Formularen zum Erfassen von Ausgaben und EinkommenAnnas eigenes Budget: Budget, Ausgaben, Einkommen und was übrig bleibt.
Persönliches Budget für Anna mit Budget, Ausgaben, Einkommen und verbleibendem Betrag sowie Formularen zum Erfassen von Ausgaben und Einkommen

Das Bereitstellen der Cloud-Seite war ein einziger Prompt: alles Nötige in Google Cloud einrichten, dazu einen GitHub-Actions-Workflow, der bei jedem Push auf main deployt. Gebaut mit Maven auf Java 21, deployt auf Cloud Run in europe-west mit CPU-Boost und --min-instances=0. Das erste Deployment funktionierte. Das Billing-Dashboard für die letzten sieben Tage zeigte 0 €.

Beim Google-Login ist der Ansatz "die KI macht alles" zerbrochen. Copilots Browser-Canvas-Ansicht, die für mich durch die Google-Cloud-Konsole klicken sollte, ist mitten im Ablauf immer wieder abgestürzt. Einmal so heftig, dass die ganze Copilot-Session weg war und ich mich neu einloggen musste. Ich bin hier etwas enttäuscht, sagte ich im Stream, bevor ich es ein letztes Mal versuchte und zu Claude Code wechselte.

Claude Code konnte den OAuth-Client auch nicht selbst anlegen, aus einem ehrlicheren Grund: Es ist ein manueller Schritt in der Google-Cloud-Konsole, ohne API-Abkürzung. Also habe ich es selbst gemacht, den Stream zweimal stummgeschaltet, um das Secret zu verbergen, und Claude die Werte dann als GitHub-Secrets speichern lassen statt sie in den Chat zu tippen.

Genau darum geht es im Diagramm oben. Jede andere Box in diesem Projekt lief über einen Prompt. Diese eine nicht, und genau dort begann das Debugging. Zuerst scheiterte ein Deployment an einem nicht beworbenen OAuth-"End-Session-Endpoint", für den ich keine saubere Lösung fand. Dann funktionierte der Login, brachte danach aber ein "Resource not found". Dann entpuppte sich ein Credential-Mismatch als Copy-Paste-Artefakt: Ich hatte das Client-Secret über die Windows-cmd statt über PowerShell kopiert, was versteckte Zeichen im Wert hinterließ. PowerShell behob es, der Login funktionierte durchgehend. Zuschauer, darunter "Suzan", "Sumi" und "Mr. SK", meldeten sich mit ihren eigenen Google-Konten an und legten eigene Daten an.

Das Billing zeigte danach wieder 0 €. Ehrliche Lücke: Das Datenmodell trennt mehrere unabhängige Familien noch immer nicht sauber. Es hat für alle Zuschauer beim Live-Test funktioniert, aber die Familien-/Reise-ID ist an Stellen noch hartkodiert, die ich selbst beim Code-Walkthrough benannt habe.

Der Cold Start hat mein eigenes Ziel verfehlt

Ich hatte mich vorbereitet: AppCDS, den Firestore-gRPC-Kanal beim Start in einem Hintergrund-Thread vorgewärmt, Cloud Runs CPU-Boost, alles auf unter zwei Sekunden ausgerichtet. Als ich die Instanz live herunter- und wieder hochfahren ließ, maß ich ungefähr sechs Sekunden, dann fünf, dann sechs, dann vier bei einem späteren Versuch, schlechter als mein Testlauf am Tag zuvor. Die Ursache habe ich nicht gefunden. Die Kostenseite hat perfekt gehalten, an der Latenz muss noch gearbeitet werden.

Abschließender Gedanke

Die Kernbehauptung hat gehalten: Eine Java-App auf Cloud Run kann auf null skalieren und im Leerlauf nichts kosten, zweimal auf der echten Billing-Seite geprüft. Überraschend war, wo der KI-gesteuerte Workflow gebrochen ist. Nicht bei der Infrastruktur, der Deploy-Pipeline oder Firestore, all das lief ohne Drama über einen Prompt, sondern bei dem einen Schritt, den Google selbst hinter einem manuellen Klick versteckt: dem Anlegen des OAuth-Clients. Genau dort häuften sich danach die Bugs. Wer sein komplettes Deployment einem KI-Agenten überlässt, sollte trotzdem einen Tab für die Konsole einplanen.

Kommentare

Kommentare optional von GitHub laden

Die Kommentarfunktion wird über Giscus und GitHub Discussions bereitgestellt. Sie wird erst nach Ihrer ausdrücklichen Einwilligung geladen. Beim Laden können personenbezogene Daten wie Ihre IP-Adresse und technische Metadaten an GitHub übermittelt sowie Cookies oder ähnliche Technologien gesetzt werden.

Bitte bestätigen Sie zuerst Ihre Einwilligung, bevor die Kommentare geladen werden.

Mehr dazu in der Datenschutzerklärung.