
AWS Lambda MicroVMs: Hands-on getestet

Nicolai Lang
Ich helfe Teams, zuverlässige und kosteneffiziente Cloud-Systeme auf AWS produktionsreif umzusetzen.
Am 22.06.2026 hat
AWS Lambda MicroVMs
gelaunched. Isolierte Compute-Instanzen auf der Firecracker-Plattform, auf der auch Lambda aufgebaut
ist. Es handelt sich aber nicht um Lambda mit längerer Laufzeit, sondern um echte VMs, die bis zu 8h
laufen können, stateful sind und mehr als einen Request parallel verarbeiten können.
Anwendungsbeispiele laut AWS sind Developer- oder AI-Agent-Sandboxes. AWS Lambda MicroVMs ist zum
Start nur in einer begrenzten Anzahl an Regionen verfügbar: us-east-1, us-east-2, us-west-2,
ap-northeast-1 und eu-west-1.
Wichtigste Fakten
- VMs basieren auf einem vorher erstellten Image-Snapshot
- Grundkosten zur Laufzeit werden anhand einer Memory- und CPU-Baseline berechnet
- Die VM "skaliert" auf das Vierfache der Baseline (mit eigenem Burst-Pricing)
- Baselines wählbar von 0,5 GB RAM / 0,25 vCPU bis 8 GB RAM / 4 vCPU, in der größten Baseline hat man solide 32 GB RAM / 16 vCPU
- Man bekommt je nach Sizing Disk Space von 8, 16 oder 32 GB
- Kosten setzen sich im Wesentlichen aus Memory (und vCPUs) und Storage zusammen
- Man zahlt mindestens die gewählte Baseline (RAM + CPU), solange die VM läuft, auch im Idle. Bei Last kommen Kosten für Burst dazu
- Compute wird sekundengenau abgerechnet
- Snapshots (VM-Images) werden pro GB/Monat abgerechnet, mit einem Minimum von einer Woche
- Suspend/Resume kosten Write/Read für den Memory-Dump
- Für den Suspend wird ein Memory- und Disk-Snapshot gespeichert, der auch Storage kostet
- Während Suspend fallen keine Kosten für Compute an, nur Speicher
- Die VM hat eine maximale Lebensdauer von 8h, in der sie ihren State behält und beliebig oft suspended und resumed werden kann, danach wird sie hart terminiert
- Startet instant und liefert einen HTTPS-Endpoint mit eigener Token-Mechanik für Auth
- Über Lifecycle-Policies können die VMs auto-suspenden, ein automatischer Resume bei eingehendem Traffic ist ebenfalls möglich und sehr schnell
Schnelleinstieg in die Funktionsweise
VM-Images
Als Erstes erzeugt man auf Basis eines Dockerfile und einer source.zip ein MicroVM-Image. Lambda
fährt dazu den Container im Hintergrund hoch und macht einen Snapshot der laufenden Anwendung im
initialisierten Zustand. Auf Basis dieses Snapshots können dann die tatsächlichen VMs erzeugt
werden. Man bekommt also, wenn man so will, immer einen "warmen" Container. Das Erzeugen eines
Images hat in meinen Tests so zwei bis drei Minuten gedauert, man kann den Prozess in CloudWatch
Logs mitverfolgen. Einstellungen, die man dem Image mitgibt, sind Memory, Umgebungsvariablen, welche
Lifecycle-Hooks feuern und welchen Timeout diese haben sollen, ein optionaler Network Connector zum
Einhängen in eine VPC, IAM Role für den Build und CloudWatch-Logging-Konfiguration. Wie von Lambda
gewohnt hängt das Sizing am Memory-Setting. Images werden automatisch versioniert (und kosten mit
steigenden Versionen auch entsprechend Speicherplatz).
MicroVMs
Nun, da wir ein Image haben, kann daraus eine VM gestartet werden. Hier wählen wir Base-Image und
Version und können eine Execution-Role und ein paar weitere Einstellungen wie Lifecycle-Policy,
LogGroup und eine individuelle Payload für den Run-Hook mitgeben. Dazu wählen wir, ob Ingress nur
HTTP oder auch Websockets für den Shell-Zugriff erlaubt sind. Für Egress kann Internet oder ein
Network Connector für VPCs gewählt werden und das war's. Die VM startet und wir bekommen für HTTP
eine URL im Format https://<ID>.lambda-microvm.<REGION>.on.aws, um mit ihr zu sprechen. Dazu
müssen wir ein Access-Token erstellen, das wir dann in einem X-Aws-Proxy-Auth Header mitschicken
und das eine wählbare Gültigkeit von bis zu 60 Minuten hat. Die Antwortzeiten lagen bei mir bei grob
35ms für einen einfachen "Hello world!" Endpoint. Nach dem Suspend mit aktiviertem Auto-Resume
dauerte der erste Request auf die suspendete VM um die 500ms, also ein sehr schneller und kaum
merklicher Resume.
Praxistest
Ich habe das Ganze mit einem kleinen NodeJS-Server getestet: Hello World, dazu eine kleine
/items-API, die nach DynamoDB schreibt und liest. Als Base-Image habe ich das von AWS empfohlene
microvms:al2023-minimal verwendet und dort noch ein paar nützliche Tools wie htop und fio
installiert. Nach dem ersten Start, dem Connecten zu einem Terminal in die VM (eingebaut in die AWS
Console) und einem htop zeigt sich sofort, dass nicht die Baseline, sondern in meinem Fall direkt
die 4 vCPUs und 8 GB Memory vorhanden sind. Es findet also in dem Sinne kein Scaling statt, sondern
es ist einfach ein Abrechnungsmodus. Wenn Du unter der Nutzung der Baseline bleibst, zahlst Du das
als fixes Minimum, alles darüber wird dynamisch abgerechnet.
Was liefert die API an Durchsatz?
Der Proxy, den AWS vor der MicroVM bereitstellt, hat feste Rate-Limits mit eingebaut. Laut der Doku sind das: 40 RPS bei 4 vCPU / 8 GB und 160 RPS bei 16 vCPU / 32 GB.
Warum hier die Burst-Werte angegeben werden, ist etwas rätselhaft. Meine eigenen Tests mit einem kleinen k6-Skript haben das auch nicht wirklich verifizieren können, und ich bin nicht an die 40 Request/s herangekommen. Selbst mit 35 rps gab es noch vereinzelte Fehler, bei 30 rps lief es sauber.
HTTP
http_req_duration..............: avg=40.82ms min=35.13ms med=38.65ms max=127.65ms p(90)=44.29ms p(95)=45.15ms
{ expected_response:true }...: avg=40.82ms min=35.13ms med=38.65ms max=127.65ms p(90)=44.29ms p(95)=45.15ms
http_req_failed................: 0.00% 0 out of 1804
http_reqs......................: 1804 29.980445/s
Die Antwortzeiten liegen in einem vernünftigen Bereich (gemessen aus Deutschland über public Internet) und grundsätzlich reicht der Durchsatz einer Instanz locker aus, um eine kleine API zu betreiben und auch den ein oder anderen Test dagegen zu fahren. Was es allerdings nicht ist, und laut AWS auch gar nicht dafür gedacht, ist ein einfacher Ersatz für ECS Fargate, um eine produktive API zu betreiben.
Snapshots, Randomness und Clock
Wie wir schon wissen, stellt die Plattform beim Starten einer MicroVM einen Snapshot wieder her, den
sie beim Image-Build gezogen hat. Der komplette Speicher taut wieder auf, inklusive PRNG-State. Für
Math.random() heißt das: Der Seed steckt im Snapshot, jede VM aus demselben Snapshot würfelt
dieselbe Sequenz.
Ich habe das mit einer kleinen /rand-API getestet. Erst hat mich das verwirrt, denn es kamen
unterschiedliche Werte zurück. Der Grund: AWS baut aktuell zwei Snapshot-Images, je eins für
Graviton 3 und Graviton 4. Das sieht man auch in den Build-Logs in CloudWatch. Jeder Snapshot
bekommt natürlich seinen eigenen Seed. Starten aber zwei VMs aus demselben Snapshot (also derselben
Graviton-Generation), sind die Abfolgen identisch. Man kann übrigens in den Logs vom run-Hook anhand
der bootId erkennen, aus welchem Snapshot die VM stammt.
crypto.randomBytes() verhält sich anders, und das hängt auch mit dem Base-Image zusammen. Node
zieht die Bytes über OpenSSL, und das al2023-Image bringt ein OpenSSL mit, das beim Resume neu
seedet (Firecracker reseedet zusätzlich den Kernel-RNG). Deshalb bekommt jede VM eigene Werte. Auf
einem fremden Base-Image wie node:24-slim seedet OpenSSL beim Resume nicht von selbst, dann
kollidiert evtl. auch crypto.randomBytes(), das habe ich aber nicht getestet. Wichtig ist also zu
wissen, auf welcher Basis man seine IDs und Tokens baut und wie der PRNG darunter geseedet wird.
Bei der Uhr ist es so, wie man es erwartet. Date gibt nach dem Resume die echte Zeit zurück, darum
musst Du Dich nicht kümmern. process.uptime() und jede andere monotone Uhr friert über die
Snapshot- und Suspend-Lücke ein: sie zählt nur die aktive Laufzeit, nicht die Zeit im eingefrorenen
Zustand. Im Normalfall ist das genau das, was man erwartet.
Disk
Für Disk gibt es kein dokumentiertes IOPS-Limit, also habe ich selbst gemessen. Verlässlich sind die Schreibwerte, weil sie über fsync gehen: sequentiell knapp 500 MiB/s, random 4k rund 100.000 IOPS. Die Lesewerte sehen mit rund 3 GB/s spektakulär aus, kommen aber großteils aus dem Host-Cache und sind deshalb weniger aussagekräftig. Worum es mir mit dem Test aber ging, war zu sehen, ob man ein vollwertiges und schnelles Dateisystem bekommt, und das ist auf jeden Fall gegeben.
Die konkreten Werte schwanken von Lauf zu Lauf, hier ein Beispiel-Run:
Logging in CloudWatch
Logging funktioniert, wie man es aus Lambda gewohnt ist: ohne zusätzlichen Aufwand über stdout nach
CloudWatch Logs. Was man wissen muss: Es funktioniert nur, wenn man dem RunMicrovm-Aufruf explizit
eine eigene Config über --logging mitgibt. Die Logging-Property am Image gilt nämlich nur für den
Build und wird nicht automatisch für die VM übernommen.
Shell-Zugang
Wer eine Shell in die VM braucht, muss beim Start SHELL_INGRESS als Ingress-Option mitgeben.
Nachträglich an einer laufenden VM attachen geht nicht. Danach öffnest Du über den Connect-Button in
der AWS-Console eine Terminal-Session. Geht natürlich auch über websocat, aber super hilfreich, sich
direkt ohne großen Aufwand verbinden zu können. Auch hier läuft Auth über einen Token.
Quotas
AWS hat auch hier, ähnlich wie bei den 10 von 1000 Concurrent Executions bei Lambda, wieder mal einen seltsamen Bock geschossen: Man bekommt (vermutlich je nach Account) stolze 8 GB von der 400-GB-Default-Quota für Memory. Das heißt, die größeren VMs kann man erst mal gar nicht starten, und selbst mit 4 GB RAM laufen nur zwei gleichzeitig. Die größeren konnte ich daher nicht testen. Mein Limit-Increase (auf 401 GB 🙄) ist noch pending und wurde leider nicht direkt von einer AI durchgewunken.
Fazit
Einen Container schnell und kostengünstig in der Cloud hochzufahren, der automatisch runterfährt, wenn Du in der Pause bist oder an etwas anderem arbeitest, aber in unter einer Sekunde wieder aufwacht, ist zum Entwickeln eine ziemlich coole Sache, egal ob Du ein Mensch oder ein Agent bist. Klar geht das auch lokal, aber sobald man in ein VPC muss, wird es wirklich interessant. Den genauen Use-Case, für den ich es in meiner täglichen Arbeit einsetzen würde, habe ich aktuell noch nicht. Aber das ist egal: Sobald man ein Tool mehr am Belt hat, kommt irgendwann der Tag, an dem man es braucht.
Den (zugegeben in großen Teilen vibe-gecodeten) Code für meine Tests findest Du als Schnellstart auf GitHub. Viel Spaß beim Ausprobieren!
