Skip to content

vidanov/lambda-microvm-starter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

12 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

CI AWS CLI Regions IaC License: MIT PRs Welcome

Any Dockerfile β†’ a real Firecracker microVM on AWS β†’ public HTTPS URL. One command.

Quick start Β· Example apps Β· How it works Β· MicroVM or Lambda? Β· Costs Β· Troubleshooting

Terminal demo: ./deploy.sh apps/playground builds a MicroVM image, launches it, and prints a public CloudFront URL

Why this exists

Lambda functions are great, right up until you need one of these:

  • state that survives between requests
  • a WebSocket that stays open
  • a job that runs longer than 15 minutes
  • to run untrusted code in real isolation
  • a full OS: background processes, dnf install, a real filesystem

The classic answer was "get an EC2 box and babysit it." AWS Lambda MicroVMs change that: full Firecracker virtual machines with Lambda ergonomics. They suspend when idle, resume in seconds when a request arrives, and cost $0 in compute while suspended.

This starter kit is the missing on-ramp. It turns "read the docs for a week" into:

./deploy.sh apps/playground

What you get:

  • πŸ–₯️ A real server: persistent processes, disk, root, your choice of runtime
  • πŸ”Œ WebSockets & long jobs: hours-long connections, no 15-minute ceiling
  • 😴 Suspend/resume built in: idle 30 min β†’ suspended β†’ auto-resumes on the next request
  • πŸ“¦ Ships like a container: if it has a Dockerfile, it deploys
  • 🌍 Public URL out of the box: CloudFront + Lambda@Edge handle HTTPS, auth, and WSS
  • πŸ”’ Or fully private: backend-only mode with short-lived auth tokens
  • πŸ—οΈ IaC included: a CDK stack and CloudFormation templates for production
  • πŸš‘ 20 gotchas pre-debugged: TROUBLESHOOTING.md is the week you don't have to lose

Quick start

Prerequisites: AWS CLI β‰₯ 2.35.10 with an authenticated session. Supported regions: us-east-1, us-east-2, us-west-2, eu-west-1, ap-northeast-1 (scripts default to eu-west-1; change REGION at the top of deploy.sh)

git clone https://github.com/vidanov/lambda-microvm-starter
cd lambda-microvm-starter

# Public: anyone can open it in a browser
./deploy.sh apps/playground

# Private: a backend API, reachable only with an auth token
./deploy.sh apps/code-runner --private
./apps/code-runner/example-client.sh   # smoke-test it

# Done playing?
./destroy.sh playground

The deploy script creates the S3 bucket and IAM roles, packages your app, builds the MicroVM image (a Firecracker snapshot), launches the VM, and, in public mode, puts CloudFront and Lambda@Edge in front of it. Then it prints your URL.

How it works

Public mode: Browser β†’ CloudFront β†’ Lambda@Edge β†’ MicroVM. Private mode: your backend mints a token and calls the MicroVM directly. Lifecycle: active β†’ suspended ($0) β†’ auto-resume

  • Public mode: CloudFront terminates HTTPS and passes WebSockets through; a Lambda@Edge function injects the MicroVM auth token on every origin request. Visitors just see a URL that works.
  • Private mode: no CloudFront at all. Your backend mints short-lived tokens (create-microvm-auth-token) and calls the MicroVM endpoint directly. Nobody without a token gets in.
  • Lifecycle: after 30 idle minutes the VM is snapshotted and suspended (configurable). The next request wakes it with memory and disk intact. You pay per second of active time only.

Example apps

Five working apps, each a single folder with a Dockerfile. Pick the one closest to what you're building:

App What it shows Mode Deploy
🎨 playground Interactive marimo notebook with live data viz, open to anyone in a browser public ./deploy.sh apps/playground
πŸ““ marimo Reactive Python notebook: sliders, charts, pip install, persistent state public ./deploy.sh apps/marimo
🐍 code-runner Sandboxed Python execution API, the classic AI-agent tool backend private ./deploy.sh apps/code-runner --private
πŸ“„ pdf-generator HTML β†’ PDF service (WeasyPrint) with an invoice template private ./deploy.sh apps/pdf-generator --private
πŸ€– shape-agent AI agent with Shape governance: phases, budgets, and an audit trail private ./deploy.sh apps/shape-agent --private
🧠 kiro AI development workspace: Kiro CLI + shell access + S3-synced workspace private ./deploy.sh apps/kiro --private

Every private app ships with an example-client.sh that mints a token and exercises the API.

API details per app

code-runner

  • POST /run: {"code": "print(1+1)", "timeout": 5} β†’ {"stdout": "2\n", "exit_code": 0, "duration_ms": 12}
  • GET /health: health check

Use it for: AI-agent tool execution, automated testing, REPL backends, code evaluation in LMS platforms.

pdf-generator

  • POST /generate: {"html": "<h1>Hello</h1>"} β†’ PDF binary
  • POST /invoice: {"company": "...", "items": [...]} β†’ formatted invoice PDF

Use it for: invoices, reports, certificates, or any other HTML-to-PDF pipeline.

shape-agent

  • POST /agent/explore: call READ tools (writes blocked)
  • POST /agent/decide: evaluate options (writes blocked)
  • POST /agent/commit: execute actions (writes allowed, budget-gated)
  • GET /agent/status: budget, time, audit trail
  • POST /agent/reset: reset agent state

Governance rules: WRITE/IRREVERSIBLE tools only in COMMIT phase, irreversible tools blocked above 75% budget, everything blocked above 90% budget or time limit. MicroVMs provide the isolation boundary; Shape controls what happens inside it.

kiro

  • POST /kiro: {"prompt": "Create a Python script"} β†’ {"output": "...", "exit_code": 0}
  • POST /shell: {"command": "ls -la", "timeout": 30} β†’ {"stdout": "...", "exit_code": 0}
  • GET /files: list workspace files
  • GET /health: health check + Kiro CLI version

Use it for: AI-agent tool backends, automated code generation, remote dev environments, CI/CD prototyping.

Bring your own app

An app is a folder with a Dockerfile that builds on the MicroVM base image and EXPOSEs a port. That's the whole contract. Python, Node, Go, whatever runs in a container:

# apps/my-app/Dockerfile
FROM public.ecr.aws/lambda/microvms:al2023-minimal
RUN dnf install -y python3 python3-pip && dnf clean all
RUN pip install --no-cache-dir flask
COPY app.py /app/app.py
WORKDIR /app
EXPOSE 5000
CMD ["python3", "app.py"]
# apps/my-app/app.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Running in a Firecracker MicroVM!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
./deploy.sh apps/my-app

The deploy script auto-detects the exposed port. The base image is ARM64 (Graviton), so fetch ARM builds when your Dockerfile downloads binaries.

MicroVM or Lambda function?

Not every workload needs a VM. The honest decision table:

Signal MicroVM Lambda function
State must survive between requests βœ…
Untrusted / user-submitted code βœ…
Runs longer than 15 minutes βœ…
WebSocket / persistent connection βœ…
Needs a full OS (FUSE, eBPF, background daemons) βœ…
High-volume, stateless βœ…
Event-driven (S3, SQS, EventBridge) βœ…
Must scale to thousands of concurrent executions βœ…

Rule of thumb: isolation + state + long runtime β†’ MicroVM. Stateless + short + high-volume β†’ Lambda function. (Example: pdf-generator works great as a demo here, but at high scale a plain Lambda function with a WeasyPrint layer is cheaper; the MicroVM version earns its keep when you need persistent caches, dependencies beyond layer limits, or renders longer than 15 minutes.)

What it costs

eu-west-1, Graviton pricing:

Usage pattern 2 vCPU / 4 GB 4 vCPU / 8 GB
Always-on 24/7 ~$96/mo ~$191/mo
4 h/day, 20 days/mo ~$11/mo ~$22/mo
Bursty with auto-suspend pay active seconds only $0 while suspended
CloudFront + Lambda@Edge ~$0-1/mo ~$0-1/mo

The economics in one sentence: MicroVMs win for anything bursty (dev environments, demos, per-user sandboxes, internal tools); for true always-on 24/7, EC2 is still 3-5x cheaper.

Production deploys: CDK & CloudFormation

deploy.sh is built for fast iteration. For repeatable, reviewable infrastructure there are two IaC paths. The AWS::Lambda::MicrovmImage resource type works in both:

Resource Managed by Why
MicroVM image, IAM, S3, CloudFront CloudFormation / CDK static, versioned infrastructure
Running MicroVMs API/SDK at runtime dynamic, per-user, ephemeral
CloudFormation: two templates

Matching the two-phase lifecycle:

  1. infra/cfn/microvm-image.yaml: image + IAM roles + S3 bucket
  2. infra/cfn/cloudfront-public.yaml: CloudFront + Lambda@Edge (needs a running MicroVM endpoint)
# Phase 1: build the image
./deploy-cfn.sh apps/playground

# Phase 2: run a MicroVM, then point CloudFront at it
aws lambda-microvms run-microvm \
  --image-identifier arn:aws:lambda:eu-west-1:ACCT:microvm-image:playground \
  --execution-role-arn arn:aws:iam::ACCT:role/MicroVMExecRole-playground \
  --idle-policy '{"maxIdleDurationSeconds":1800,"suspendedDurationSeconds":28800,"autoResumeEnabled":true}' \
  --region eu-west-1

aws cloudformation deploy \
  --template-file infra/cfn/cloudfront-public.yaml \
  --stack-name microvm-playground-cdn \
  --parameter-overrides \
    MicrovmId=microvm-XXXX \
    MicrovmEndpoint=XXXX.lambda-microvm.eu-west-1.on.aws \
    AppPort=2718 \
  --capabilities CAPABILITY_NAMED_IAM \
  --region us-east-1
CDK: one stack, including the MicroVM lifecycle
cd infra/cdk
pip install aws-cdk-lib constructs

# Bundle the orchestrator's boto3 (includes the lambda-microvms service model)
./orchestrator/build.sh

# Upload your app code
aws s3 cp app.zip s3://microvm-artifacts-ACCT-eu-west-1/images/playground.zip

# Deploy everything: image build β†’ run MicroVM β†’ CloudFront
cdk deploy -c app_name=playground -c app_port=2718 --profile YOUR_PROFILE

The stack uses a custom-resource orchestrator Lambda that runs the MicroVM, creates the Lambda@Edge function in us-east-1 (a CloudFront requirement), bakes the MicroVM ID into it, and wires it into CloudFront. cdk destroy tears all of it down.

Implementation notes worth knowing:

  • The orchestrator bundles its own boto3 with the lambda-microvms service model (not yet in the stock SDK)
  • IAM actions live in the lambda: namespace (e.g. lambda:RunMicrovm), not lambda-microvms:
  • Lambda@Edge must be in us-east-1, so the orchestrator creates it cross-region

VPC access

MicroVMs don't sit inside your VPC (same model as Lambda functions). To reach private resources, attach a Lambda Network Connector; outbound traffic then flows through your subnets:

aws lambda-microvms run-microvm \
  --image-identifier ... \
  --egress-network-connectors '["arn:aws:lambda:REGION:ACCT:network-connector:my-vpc-connector"]'

Default is INTERNET_EGRESS (public internet).

Local development

Every app runs locally without AWS:

cd apps/playground && pip install -r requirements.txt && marimo edit app.py
cd apps/code-runner && pip install -r requirements.txt && uvicorn main:app --port 8080

Cleanup

./destroy.sh playground        # tears down the MicroVM (+ CloudFront if public)

# or manually:
aws lambda-microvms terminate-microvm --microvm-identifier MICROVM_ID --region eu-west-1

When it breaks

It's a new service; sharp edges exist. TROUBLESHOOTING.md documents 20 real failures with fixes: IAM trust policies, WebSocket proxying, Lambda@Edge region traps, CDK custom-resource timeouts. Every entry is something I actually hit while building this kit. If you hit something new, open an issue.

Project structure

β”œβ”€β”€ apps/                    # Example apps (one folder + Dockerfile each)
β”‚   β”œβ”€β”€ playground/          #   marimo notebook (public)
β”‚   β”œβ”€β”€ marimo/              #   reactive notebook: sliders, charts (public)
β”‚   β”œβ”€β”€ code-runner/         #   sandboxed Python execution API (private)
β”‚   β”œβ”€β”€ pdf-generator/       #   HTML β†’ PDF service (private)
β”‚   β”œβ”€β”€ shape-agent/         #   governed AI agent (private)
β”‚   └── kiro/                #   AI dev workspace with Kiro CLI (private)
β”œβ”€β”€ infra/
β”‚   β”œβ”€β”€ cfn/                 # CloudFormation templates (image, CloudFront)
β”‚   β”œβ”€β”€ cdk/                 # CDK stack + custom-resource orchestrator
β”‚   └── edge-auth/           # Lambda@Edge auth-token injector
β”œβ”€β”€ docs/                    # README assets
β”œβ”€β”€ deploy.sh                # One-command CLI deploy (fast iteration)
β”œβ”€β”€ deploy-cfn.sh            # CloudFormation deploy (production)
β”œβ”€β”€ destroy.sh               # Tear a deployment down
└── TROUBLESHOOTING.md       # 20 gotchas, pre-debugged

Contributing

The most valuable contribution is a new example app. The pattern that fits is isolation + state + long runtime. Ideas where a MicroVM beats a Lambda function:

Idea Why a MicroVM Effort
playwright-runner pre-loaded Chromium in the snapshot = no cold browser start medium
jupyter-workspace per-user notebooks with pip install + persistent files medium
game-server stateful multiplayer over WebSockets medium
ci-runner isolated builds, Docker-in-Docker medium
vulnerability-scanner untrusted tooling against customer code low
llm-sandbox per-tenant local LLM (Ollama / llama.cpp) high

Fork, branch, PR. A bug report with the failing deploy.sh log attached is gold.

See CONTRIBUTING.md for details.

License

MIT. Do whatever you want with it. If it saved you a debugging week, a star ⭐ helps the next person find it.

About

Deploy any web app to an AWS Lambda MicroVM with public CloudFront access. One command.

Resources

License

Contributing

Stars

4 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors