Architekturentscheidung mit Kostenfolge

Serverless oder Container: Wann Functions sparen und wann sie teuer werden

Bei stoßweiser Last und wenigen Millionen Aufrufen im Monat sind Functions fast immer die günstigere Wahl, oft sogar kostenlos. Läuft dein Dienst dagegen rund um die Uhr mit gleichmäßiger Auslastung, gewinnt der dauerhaft laufende Container, und zwar deutlich. Die Entscheidung fällt am Lastprofil und an den Laufzeitgrenzen, nicht an der Sympathie für eine Technik.

5 Kapitel mit allen Befehlen
Zwei IT-Entscheider vergleichen Plattformoptionen an einem großen Bildschirm
Seit 1997 am Markt Präsenz & Live-Online 4,9 aus 503 Google-Bewertungen Auch Inhouse für dein Team
Worum es geht

Warum die Serverless-Rechnung so oft anders ausfällt als geplant

Serverless wird über den Preis verkauft, und für den Einstieg stimmt das Versprechen auch. Du zahlst je Aufruf und je Gigabyte-Sekunde Laufzeit, im Leerlauf zahlst du nichts. Ein Formularversand mit zwanzigtausend Aufrufen im Monat bleibt bei AWS Lambda und bei Azure Functions komplett im dauerhaft kostenlosen Kontingent. Der Denkfehler entsteht beim Hochrechnen, denn die Kosten wachsen linear mit der Nutzung, während ein Container ab einer bestimmten Auslastung nichts mehr zusätzlich kostet. Ab dem Schnittpunkt zahlst du für dieselbe Arbeit ein Vielfaches.

Der zweite Kostenblock steht selten in der Rechnung, sondern im Projektplan. Zustandslosigkeit ist keine Formalie, sondern eine Anforderung an den Code: keine Sitzungen im Arbeitsspeicher, keine lokal geschriebenen Dateien, die den nächsten Aufruf überleben, keine dauerhaft offenen Datenbankverbindungen. Genau der letzte Punkt kippt regelmäßig, weil hunderte parallel gestartete Funktionsinstanzen hunderte Verbindungen zur Datenbank aufbauen und diese in die Knie zwingen. Der Ausweg heißt Verbindungspool als eigener Dienst, und der kostet wieder Geld und Betrieb.

Dazu kommen harte Grenzen. Eine Lambda-Funktion läuft maximal 15 Minuten und bekommt höchstens 10 Gigabyte Arbeitsspeicher, im Verbrauchsplan von Azure Functions endet die Ausführung nach spätestens zehn Minuten. Ein nächtlicher Import über vier Stunden ist damit erledigt, bevor die Diskussion beginnt. Und der Rückweg ist teurer als gedacht: Fachlogik lässt sich portieren, aber Auslöser, Berechtigungsmodell, Protokollierung und Ablaufsteuerung sind bei jedem Anbieter anders gebaut. Wer die Anbieterbindung klein halten will, hält die Funktionen klein und die Fachlogik in eigenen Bibliotheken.

Miniatur-Szene: zwei Plattformen nebeneinander mit unterschiedlichen Cloud-Bausteinen, dazwischen eine Waage

Sechs Größen, die die Entscheidung tragen

  1. 01 Aufrufe pro Monat und Verteilung über den Tag
  2. 02 Mittlere Laufzeit und belegter Speicher je Aufruf
  3. 03 Zulässige Antwortzeit für den ersten Aufruf nach Ruhe
  4. 04 Laufzeitgrenze, Speicherbedarf und Zustandshaltung je Aufruf
  5. 05 Verbindungen zur Datenbank bei paralleler Ausführung
  6. 06 Aufwand für den Rückweg zu einem anderen Anbieter
Was du mitnimmst

Vier Betriebsmodelle und die Stellen, an denen sie brechen

Zwischen Functions und dauerhaft laufenden Containern liegen zwei Zwischenstufen, die in der Diskussion meist untergehen. Sie sind für viele Fachverfahren die eigentlich richtige Antwort.

Functions im Verbrauchsmodell

AWS Lambda und Azure Functions im Consumption Plan rechnen je Aufruf und je Gigabyte-Sekunde ab und kosten im Leerlauf nichts. Ideal für Ereignisverarbeitung, Webhooks, geplante Aufgaben und alles, was in Schüben kommt. Bruchstellen sind Kaltstarts von einigen hundert Millisekunden bis mehreren Sekunden, die Laufzeitgrenze und der Verbindungsaufbau zur Datenbank bei paralleler Ausführung.

Container mit Skalierung auf null

Azure Container Apps und AWS Fargate hinter einer ereignisgesteuerten Skalierung fahren die Anzahl der Instanzen bis auf null herunter und starten bei Bedarf wieder hoch. Du behältst dein Container-Image, deine Laufzeitumgebung und deine Bibliotheken, zahlst aber nur für belegte Ressourcen. Der Preis dafür ist ein Kaltstart im Sekundenbereich, spürbar länger als bei einer schlanken Funktion.

Dauerhaft laufende Container

Eine feste Anzahl Aufgaben in ECS mit Fargate oder ein Deployment in Kubernetes kostet unabhängig von der Last, und genau das ist der Vorteil bei gleichmäßiger Auslastung. Keine Kaltstarts, keine Laufzeitgrenze, offene Verbindungspools, freie Wahl bei Sprache und Bibliotheken. Der Aufwand liegt im Betrieb: Skalierung, Aktualisierungen, Netz und Beobachtbarkeit gehören dir.

Kubernetes mit ereignisgesteuerter Skalierung

Wer ohnehin einen Cluster betreibt, bekommt mit KEDA den Verbrauchseffekt im eigenen Cluster: Skalierung nach Warteschlangenlänge, Themen im Nachrichtenbus oder eigenen Metriken, bis hinunter auf null Instanzen. Sinnvoll, wenn der Cluster schon steht und ausgelastet ist. Als Grund, einen Cluster überhaupt erst aufzubauen, trägt das Argument nicht.

Die Rechnung, die du selbst machen musst

Nimm die Zahl der Aufrufe im Monat, die mittlere Laufzeit, den tatsächlich belegten Speicher und das Tagesprofil. Daraus ergibt sich der Schnittpunkt zwischen beiden Modellen. Er liegt bei typischen Web-Interaktionen häufig im Bereich einiger Millionen Aufrufe pro Monat, verschiebt sich aber stark mit der Laufzeit je Aufruf. Lange Aufrufe machen Functions früh teuer.

Obergrenzen gehören vor den Produktivgang

Serverless skaliert auch dann, wenn es das nicht sollte, etwa bei einem fehlerhaften Auslöser oder einer Schleife über eine Warteschlange. Eine Obergrenze für die parallele Ausführung, eine Zeitgrenze je Aufruf und ein Budgetalarm kosten zehn Minuten und verhindern die Rechnung, über die andere später Vorträge halten. Beim dauerhaft laufenden Container übernimmt diese Rolle die feste Anzahl an Instanzen, die du ohnehin festlegst.

Tutorial

Vom gemessenen Lastprofil zur belastbaren Kostenrechnung

Bei stoßweiser Last und wenigen Millionen Aufrufen im Monat gewinnen Functions, bei gleichmäßiger Auslastung rund um die Uhr der dauerhaft laufende Container. Wo genau der Schnittpunkt liegt, hängt an der Laufzeit je Aufruf, deshalb wird hier zuerst gemessen, dann gerechnet, dann werden Kaltstarts und Grenzen getestet, der Mittelweg geprüft und zum Schluss Notbremsen eingebaut. Rechne alles gegen echte Zahlen aus deinem Betrieb, geschätzte Aufrufzahlen liegen erfahrungsgemäß um den Faktor zwei bis zehn daneben.

01

Schritt 1: Das Lastprofil messen statt schätzen

Ohne echte Zahlen ist jede Kostenrechnung eine Meinung. Du brauchst vier Werte: Aufrufe pro Monat, die Verteilung über den Tag, die Laufzeitverteilung und den tatsächlich belegten Speicher.

Aufrufe und Laufzeiten aus den letzten 30 Tagen holen
# Wie viele Aufrufe pro Tag? Zeigt sofort das Wochen- und Tagesprofil
aws cloudwatch get-metric-statistics \
  --namespace AWS/Lambda --metric-name Invocations \
  --dimensions Name=FunctionName,Value=bestellungen \
  --start-time 2026-06-25T00:00:00Z --end-time 2026-07-25T00:00:00Z \
  --period 86400 --statistics Sum

# Laufzeitverteilung statt Mittelwert, plus tatsaechlich belegter Speicher
aws logs start-query \
  --log-group-name /aws/lambda/bestellungen \
  --start-time 1750809600 --end-time 1753401600 \
  --query-string 'filter @type = "REPORT"
    | stats count(*) as aufrufe,
            pct(@duration, 50) as p50,
            pct(@duration, 95) as p95,
            max(@duration) as maximum,
            avg(@maxMemoryUsed / 1000 / 1000) as mb_belegt'

Der Mittelwert der Laufzeit ist für die Kostenrechnung fast wertlos. Entscheidend sind das 95. Perzentil und der belegte Speicher, denn beide bestimmen, wie viel Speicher du zuweisen musst und damit den Preis je Aufruf.

Vier typische Lastprofile und was sie kosten

ProfilFunctionsDauerhaft laufender ContainerWahl
Formularversand, 20.000 Aufrufe im Monat, 300 msim dauerhaft kostenlosen Kontingent, praktisch nullkleinste Aufgabe rund um die Uhr, gut 10 USD im MonatFunctions, ohne Diskussion
Interner Dienst, werktags 8 bis 18 Uhr, 2 Mio. Aufrufewenige Euro, nachts und am Wochenende kostenfreiläuft auch nachts durch und kostet dabei weiterFunctions oder Skalierung auf null
Öffentliche API, 50 Aufrufe je Sekunde rund um die Uhrrund 130 Mio. Aufrufe, im dreistelligen Bereichzwei feste Aufgaben genügen, etwa die Hälfte davonContainer, plus stabilere Antwortzeiten
Nächtlicher Import, vier Stunden, 16 GB Speichernicht möglich, Laufzeit- und Speichergrenze greifeneine Aufgabe je Nacht, wenige Euro im MonatContainer oder ein Batch-Dienst
02

Schritt 2: Beide Wege gegenrechnen

Die Preismodelle sind bei AWS und Azure erstaunlich ähnlich aufgebaut: ein Betrag je Million Aufrufe plus ein Betrag je Gigabyte-Sekunde, dazu ein dauerhaft kostenloses Kontingent. Damit lässt sich der Schnittpunkt in wenigen Zeilen ausrechnen.

Schnittpunkt selbst ausrechnen
python3 - <<'EOF'
# Werte aus der eigenen Messung eintragen
aufrufe_monat = 130_000_000
speicher_gb   = 0.5
laufzeit_s    = 0.1

# Groessenordnungen der Anbieter, aktuelle Preisseite gegenpruefen
preis_je_mio_aufrufe = 0.20      # USD
preis_je_gb_sekunde  = 0.0000167 # USD

functions = (aufrufe_monat / 1_000_000) * preis_je_mio_aufrufe \
          + aufrufe_monat * speicher_gb * laufzeit_s * preis_je_gb_sekunde

# Dauerhafter Container: zwei Aufgaben mit je 1 vCPU und 2 GB
stunden = 730
container = 2 * (stunden * 0.04 + stunden * 2 * 0.005)

print(f"Functions:  {functions:8.2f} USD im Monat")
print(f"Container:  {container:8.2f} USD im Monat")
EOF

Die eingetragenen Preise sind Größenordnungen für europäische Regionen und ändern sich. Was sich nicht ändert, ist die Struktur der Rechnung: Functions wachsen linear mit der Nutzung, Container kosten eine Konstante. Der Schnittpunkt verschiebt sich vor allem mit der Laufzeit je Aufruf.

Zwei Faktoren senken die Rechnung auf der Functions-Seite spürbar, bevor du das Modell wechselst. Erstens die Prozessorarchitektur: Auf ARM ausgeführte Funktionen kosten bei AWS rund ein Fünftel weniger als auf x86, und der Wechsel ist bei interpretierten Sprachen meist eine Zeile in der Konfiguration. Zweitens die Speicherzuweisung, denn mehr Speicher bedeutet auch mehr Rechenleistung. Eine Funktion mit 1024 Megabyte kann so schnell fertig sein, dass sie trotz doppeltem Speicherpreis günstiger ist als dieselbe Funktion mit 512 Megabyte. Das lässt sich nur messen, nicht herleiten. Die Gesamtsicht auf solche Effekte über alle Dienste hinweg beschreibt Cloud-Kosten analysieren.

03

Schritt 3: Kaltstarts und Grenzen ehrlich testen

Kaltstarts sind der häufigste Grund, warum ein technisch passendes Serverless-Konzept am Ende doch abgelehnt wird. Messen ist einfach, die Zahlen sind eindeutig.

Kaltstartanteil und Kaltstartdauer sichtbar machen
# Nur Kaltstarts liefern das Feld initDuration
aws logs start-query \
  --log-group-name /aws/lambda/bestellungen \
  --start-time 1753142400 --end-time 1753401600 \
  --query-string 'filter @message like /Init Duration/
    | stats count(*) as kaltstarts,
            avg(@initDuration) as schnitt_ms,
            pct(@initDuration, 95) as p95_ms'

# Bereitgestellte Nebenlaeufigkeit gegen Kaltstarts, kostet aber rund um die Uhr
aws lambda put-provisioned-concurrency-config \
  --function-name bestellungen --qualifier live \
  --provisioned-concurrent-executions 5

# Azure: derselbe Effekt heisst hier vorgewaermte Instanzen im Premium-Plan
az functionapp plan update -g rg-shop -n plan-shop --min-instances 1

Setze den Kaltstartanteil ins Verhältnis: Bei einer stark genutzten Funktion sind oft weniger als ein Prozent der Aufrufe betroffen. Erst wenn dein Dienst schubweise kommt und jeder Schub den ersten Nutzer trifft, wird das zum Problem.

04

Schritt 4: Den Mittelweg prüfen

Zwischen Function und Dauerbetrieb liegt der Container, der auf null herunterfährt. Du behältst dein Image und deine Laufzeitumgebung und zahlst trotzdem nur für tatsächliche Nutzung.

Container mit Skalierung auf null in Azure und AWS
# Azure Container Apps: faehrt bei Ruhe auf null Instanzen herunter
az containerapp create \
  -g rg-shop -n bestellungen --environment env-shop \
  --image ghcr.io/example/bestellungen:1.4.2 \
  --target-port 8080 --ingress external \
  --min-replicas 0 --max-replicas 10 \
  --cpu 0.5 --memory 1.0Gi \
  --scale-rule-name http --scale-rule-type http \
  --scale-rule-http-concurrency 50

# AWS: ECS mit Fargate und Skalierung nach Warteschlangenlaenge
aws application-autoscaling register-scalable-target \
  --service-namespace ecs --scalable-dimension ecs:service:DesiredCount \
  --resource-id service/shop/bestellungen --min-capacity 0 --max-capacity 10

Achte auf die Startzeit deines Images. Ein Java-Dienst mit 40 Sekunden Startzeit ist für Skalierung auf null ungeeignet, solange Nutzer synchron auf die Antwort warten. Für ereignisgesteuerte Verarbeitung aus einer Warteschlange spielt das dagegen keine Rolle.

Wenn du dich hier für Kubernetes mit ereignisgesteuerter Skalierung entscheidest, prüfe zuerst, ob der Cluster für andere Aufgaben ohnehin steht. Einen Cluster allein deswegen aufzubauen, dreht die Kostenrechnung um: Zur Rechnung des Anbieters kommen dann Steuerungsebene, Knoten im Leerlauf, Netzrand und die Arbeitszeit für Aktualisierungen. Die Abwägung dahinter beschreibt brauche ich Kubernetes im Detail. Für einen einzelnen Dienst ist ein verwalteter Container-Dienst fast immer die günstigere und schnellere Antwort.

05

Schritt 5: Notbremsen einbauen, bevor produktiv geschaltet wird

Serverless skaliert auch dann, wenn es das nicht sollte. Zwei Obergrenzen und ein Alarm kosten dich zehn Minuten und verhindern die Rechnung, über die andere später Vorträge halten.

Obergrenze je Funktion und Budgetalarm auf Kontoebene
# Harte Obergrenze: mehr als 50 gleichzeitige Ausfuehrungen gibt es nicht
aws lambda put-function-concurrency \
  --function-name bestellungen --reserved-concurrent-executions 50

# Budgetalarm fuer das ganze Konto
aws budgets create-budget \
  --account-id "$(aws sts get-caller-identity --query Account --output text)" \
  --budget file://budget.json \
  --notifications-with-subscribers file://alarm.json

# Azure: Obergrenze der Instanzen und Alarm auf die Metrik der Container App
az containerapp update -g rg-shop -n bestellungen --max-replicas 10
az monitor metrics alert create -g rg-shop -n bestellungen-last \
  --scopes "$(az containerapp show -g rg-shop -n bestellungen --query id -o tsv)" \
  --condition "avg Replicas > 8" --window-size 5m --evaluation-frequency 1m

Eine reservierte Obergrenze für die Nebenläufigkeit begrenzt nicht nur die Kosten, sondern schützt auch alles, was hinter der Funktion liegt: Datenbank, fremde Schnittstellen und Lizenzkontingente.

Bevor die erste Funktion produktiv geht

  • Reservierte Obergrenze für die Nebenläufigkeit je Funktion setzenOhne sie skaliert ein Fehler im Auslöser bis an das Kontolimit und erzeugt in Stunden Kosten, die im Monatsbudget nicht vorgesehen waren.
  • Ein- und Ausgangsbereich für Dateien strikt trennenSchreibt eine Funktion ihr Ergebnis dorthin zurück, wo ihr Auslöser lauscht, entsteht eine Schleife, die sich selbst befeuert.
  • Budgetalarm mit Empfänger einrichten und einmal auslösenEin Alarm, der nur konfiguriert und nie getestet wurde, ist im Ernstfall genau so viel wert wie kein Alarm.
  • Verbindungspool oder Nebenläufigkeitsgrenze vor der DatenbankHunderte gleichzeitige Instanzen erschöpfen den Verbindungspool der Datenbank und legen damit auch alle anderen Anwendungen lahm, die daran hängen.
  • Zeitgrenze je Funktion bewusst setzen statt auf dem Standardwert lassenEine hängende Funktion mit 15 Minuten Zeitgrenze zahlst du 15 Minuten lang, obwohl sie nach zwei Sekunden bereits fachlich gescheitert war.
  • Ablaufverfolgung und strukturierte Protokolle von Anfang anOhne Server gibt es nichts, worauf du dich einloggen kannst. Was du nicht vorher protokollierst, ist im Störungsfall unwiederbringlich weg.
Gut zu wissen

Häufige Fragen zu Serverless und Containern

Noch etwas offen? Wir sind ohne Warteschleife für dich da.

Frag uns direkt
Ab wann ist ein Container günstiger als Functions?
Es gibt keine allgemeine Zahl, aber eine verlässliche Faustregel: Der Schnittpunkt liegt dort, wo die Summe der bezahlten Gigabyte-Sekunden die Kosten einer dauerhaft laufenden Aufgabe übersteigt. Bei kurzen Aufrufen von rund 100 Millisekunden mit 512 Megabyte liegt das im Bereich einiger Millionen Aufrufe pro Monat. Bei Aufrufen von zwei Sekunden ist derselbe Punkt zwanzigmal früher erreicht. Rechne deshalb immer mit deiner gemessenen Laufzeit und deinem gemessenen Speicherbedarf, wie im zweiten Schritt gezeigt, und nicht mit Beispielen aus Blogbeiträgen.
Wie schlimm sind Kaltstarts wirklich?
Das hängt an Sprache und Paketgröße. Schlanke Funktionen in Python, Node oder Go starten in der Regel in einigen hundert Millisekunden, Java und .NET brauchen ohne Vorwärmen deutlich länger. Entscheidend ist der Anteil: Bei gleichmäßiger Last sind oft weniger als ein Prozent der Aufrufe betroffen, bei schubweiser Last trifft es dagegen fast jeden ersten Nutzer eines Schubs. Gegenmittel gibt es, etwa bereitgestellte Nebenläufigkeit bei AWS oder vorgewärmte Instanzen bei Azure. Beide kosten allerdings rund um die Uhr und heben damit einen Teil des Serverless-Vorteils wieder auf.
Wie stark binde ich mich an einen Anbieter?
Stärker als bei Containern, aber nicht so stark wie oft behauptet. Die Fachlogik selbst ist portabel, sofern du sie in eigenen Modulen hältst und die Funktion nur als dünne Schale darum baust. Fest verdrahtet sind dagegen Auslöser, Berechtigungsmodell, Protokollierung, Ablaufsteuerung und die Infrastrukturbeschreibung. Rechne für eine mittelgroße Anwendung mit einigen Wochen für einen Anbieterwechsel. Wer diese Bindung bewusst klein halten will, hält Funktionen klein, kapselt Anbieterdienste hinter eigenen Schnittstellen und verzichtet auf tief integrierte Ablaufsteuerungen.
Kann ich Serverless und Container mischen?
Das ist sogar der Normalfall in gewachsenen Umgebungen. Die dauerhaft belastete Kernanwendung läuft im Container, während Randaufgaben als Funktionen ausgeführt werden: Bildverarbeitung nach dem Hochladen, nächtliche Berichte, Webhooks von Fremdsystemen, Aufräumarbeiten. Diese Aufteilung folgt genau dem Kostenmodell und ist deshalb dauerhaft tragfähig. Wichtig ist nur, dass beide Teile dieselbe Protokollierung und dieselbe Ablaufverfolgung nutzen. Sonst hast du zwei getrennte Sichten auf ein System und im Störungsfall keine durchgehende Spur.
Was ist mit langlaufenden Aufgaben?
Sie sind das klarste Ausschlusskriterium für Functions. AWS Lambda endet nach 15 Minuten, Azure Functions im Verbrauchsplan bereits nach spätestens zehn Minuten. Ein vierstündiger Import ist damit erledigt. Es gibt zwei Auswege: die Aufgabe in kleine Teilschritte zerlegen und über eine Ablaufsteuerung koordinieren, oder sie schlicht in einen Container legen. Der zweite Weg ist meistens der ehrlichere, weil das Zerlegen einer Stapelverarbeitung in fünfzig Funktionsaufrufe zwar technisch geht, den Betrieb aber komplizierter macht als der Ausgangszustand.

Zuletzt geprüft am 26. Juli 2026.

Das sagen Teilnehmer

Echte Stimmen aus unseren IT-Kursen

Sehr kompetenter Dozent der gut auf alle Fragen eingegangen ist.
Rückmeldung aus dem Kurs „Kubernetes Grundkurs“
Erwartungen wurden erfüllt. Angekündigte Themen in hinreichender Tiefe bearbeitet. Zudem gutes Zeitmanagement.
Rückmeldung aus dem Kurs „Source Code Management und CI / CD (DO4)“
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Cloud-Programm den passenden Kurs oder Lernpfad zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die exakt auf eure Systemlandschaft und Distributionen zugeschnitten sind.

Nächster Schritt

Die Entscheidung an deinem eigenen Fachverfahren durchspielen

In den Kursen zu AWS Lambda, Azure Container Apps und Kubernetes rechnest du beide Wege an konkreten Lastprofilen durch und baust jeweils eine lauffähige Variante. Weil cmt alle drei Richtungen im Programm hat, bekommst du eine Empfehlung statt einer Produktvorstellung. Die Kurse laufen Live-Online oder in Präsenz, auf Wunsch als Inhouse-Termin mit euren eigenen Zahlen als Grundlage.