npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

uvdock

v0.1.0

Published

Dock your code into an AWS Lambda MicroVM. Bring an existing Lambda handler, attach it, and get a running, stateful VM — a tiny PID-1 runtime bridges HTTP to the Lambda handler contract (Function URL events + invoke mode + lifecycle hooks), so your handle

Downloads

19

Readme

uvdock

Dock your code into an AWS Lambda MicroVM. Bring a Lambda handler, attach it, get a running — and stateful — VM.

CI release license node

MicroVMs are powerful but raw — your app runs as PID 1 on ARM64, there's no invoke API, and you have to answer platform lifecycle hooks. uvdock makes them usable: bring your existing code, dock it, and you get a running VM you can invoke like a function. It ships with a Lambda adapter as the first way to bring code — a tiny, zero-dependency PID-1 runtime that lets an existing handler(event, context) run exactly as written, but now keeping its memory warm between calls. (The optional invoke client depends on uvlink for auth-token handling; the runtime that ships inside the VM stays dependency-free.)

Part of the uv* MicroVM toolkit — sibling to uvlink (reach a MicroVM from a browser). uvlink gets you to a MicroVM; uvdock gets your code running inside one.

The Lambda adapter is the inverse of aws-lambda-web-adapter: that runs a web server on Lambda; this runs a Lambda handler as a server on a MicroVM.

Hobby project. Built for fun and learning, not affiliated with or endorsed by AWS / Lambda / Amazon. MIT-licensed — free for anyone to use, fork, and modify. Provided as-is, with no warranty and no guarantee of future releases, maintenance, or support.


Why

Lambda MicroVMs are Firecracker-isolated VMs that suspend when idle and resume with memory and disk intact, each with its own HTTPS endpoint. But they're an alien target: your process runs as PID 1 on ARM64, there's no invoke API (you reach it over HTTP with a token), and you must answer the platform's lifecycle hooks. A raw Lambda zip can't run there.

This adapter is the missing glue. The payoff: a "slim, stateful Lambda" — the same handler you already have, but in-memory caches, loaded models, and open connections persist across invocations instead of being thrown away.

How it works

        Lambda zip in S3                          Running MicroVM
   (code + handler, unchanged)          ┌───────────────────────────────┐
              │                         │  bootstrap.mjs  (PID 1)        │
              │  build.sh               │    HTTP :8080  ─┐              │
              ▼                         │                 ▼              │
   unzip → /app  +  bootstrap.mjs  ───► │   translate → handler(event,ctx)
   +  generated Dockerfile              │                 │              │
              │                         │    response  ◄──┘              │
              ▼                         │    lifecycle hooks (/run …)    │
     create-microvm-image               └───────────────────────────────┘

build.sh never runs your code — it repackages: unzip the Lambda deployment package into /app, drop in the adapter, generate a Dockerfile, and hand it to create-microvm-image. At runtime the adapter serves HTTP, turns each request into a Lambda Function URL v2.0 event, calls your handler, and turns the return value back into a response.

Quick start

Point it at a Lambda deployment package in your own S3 bucket — the same object Lambda self-managed code storage uses.

REGION=us-east-1 ACCOUNT=123456789012 BUCKET=my-bucket \
LAMBDA_S3_URI=s3://my-bucket/functions/my-fn.zip \
HANDLER=index.handler \
./build.sh
# → prints IMAGE_ARN

Then run it and invoke it:

aws lambda-microvms run-microvm --region us-east-1 --image-identifier <IMAGE_ARN> \
  --ingress-network-connectors "arn:aws:lambda:us-east-1:aws:network-connector:aws-network-connector:ALL_INGRESS" \
  --egress-network-connectors  "arn:aws:lambda:us-east-1:aws:network-connector:aws-network-connector:INTERNET_EGRESS"

node invoke.mjs <microvmId> '{"numbers":[1,2,3,4,5]}'
# → { "sum": 15, "count": 5, "invocations": 1 }

Try it locally (no AWS)

APP_DIR="$PWD/example" HANDLER=index.handler PORT=8099 node runtime/bootstrap.mjs &
curl -s "http://127.0.0.1:8099/hello?name=world"                   # HTTP mode
curl -s -XPOST "http://127.0.0.1:8099/2015-03-31/functions/function/invocations" -d '{"numbers":[1,2,3]}'  # invoke mode

The stateful payoff

invoke.mjs is a lambda.invoke()-shaped client. The difference — and the reason to use this:

| | lambda.invoke() | invokeMicrovm() | |---|---|---| | Capacity | conjured by the service | a VM you already ran (you own its lifecycle) | | State between calls | none | preserved — module state, warm caches, loaded models persist | | First call after idle | cold start | auto-resume from suspend (once) | | Isolation | shared Firecracker fleet | one dedicated Firecracker VM |

import { invokeMicrovm } from "uvdock";
const out = await invokeMicrovm("mvm-01234...", { numbers: [1, 2, 3] }, { region: "us-east-1" });

Two invoke modes

| Mode | Path | event your handler sees | Response | |---|---|---|---| | Invoke (passthrough) | POST /2015-03-31/functions/function/invocations | your payload, verbatim | return value, verbatim JSON | | HTTP (Function URL v2.0) | any other path/method | Function URL v2.0 request envelope | your {statusCode, headers, body, cookies} |

Invoke mode intentionally mirrors the Lambda Runtime Interface Emulator (RIE) contract, so existing tooling and curl recipes work unchanged.

Configuration

Set via env vars (baked into the image by build.sh, or exported for local runs).

build.sh

| Var | Required | Default | Meaning | |---|---|---|---| | REGION ACCOUNT BUCKET | ✓ | — | AWS target + bucket for build context | | LAMBDA_S3_URI | ✓ | — | s3://…/fn.zip — your Lambda deployment package | | HANDLER | | index.handler | file.export to load from /app | | IMAGE_NAME | | uvdock | MicroVM image name | | BUILD_ROLE | | MicrovmBuildRole | build role (needs s3:GetObject on BUCKET — see gotchas) | | NODE_TAG | | node:22-bookworm-slim | container base image | | ENV_VARS | | — | comma-separated K=V baked into the image |

Adapter runtime

| Var | Default | Meaning | |---|---|---| | HANDLER | index.handler | which export to invoke | | PORT | 8080 | MicroVM default ingress port | | ADAPTER_CONCURRENCY | 1 | max concurrent invocations. Default 1 matches Lambda's one-at-a-time model. Set N (or 0 = unbounded) only if your handler is reentrant | | MAX_BODY_BYTES | 6291456 (6 MB) | request body cap (Lambda's sync payload limit) |

Optional lifecycle hooks

Export any of these from your handler module and the adapter calls them; omit them and it uses safe 200 defaults:

export async function onRun(payload)  {}  // { microvmId, runHookPayload } — mint per-VM identity HERE
export async function onSuspend()     {}  // flush before snapshot
export async function onResume()      {}  // refresh creds / reconnect
export async function onTerminate()   {}

Snapshot-safety: anything unique-per-VM (a session id, secret, tenant token) must be generated in onRun, never at import/build time — every VM resumed from one snapshot shares build-time state byte-for-byte.

Compatibility & limits

| ✓ Works | ✗ Not supported | |---|---| | CommonJS (exports.handler) and ESM (export const handler) | Non-Node runtimes (Python, Go, …) — the pattern generalizes, this code doesn't | | Request/response handlers (Function URL / API Gateway v2.0 payload) | Event-source handlers (SQS/S3/EventBridge) — no HTTP request to translate from | | async handlers returning {statusCode,…} or a bare object/string | API Gateway v1.0 payloads (event.httpMethod), response streaming, layers, extensions | | isBase64Encoded bodies, cookies[] responses, context.getRemainingTimeInMillis() | X-Ray, real CloudWatch log-stream identity, provisioned concurrency |

Similar projects & when not to use this

Be honest about the alternatives — reach for this only when its specific shape fits.

  • Lambda Managed Instances — AWS's first-party "warm, no-cold-start, unchanged code" mode. If you want a fully managed, autoscaling, load-balanced warm Lambda and don't need per-tenant isolation, use this instead — it runs on EC2 Nitro containers with dynamic scaling. Reach for uvdock when you specifically want one Firecracker-isolated VM per tenant/session, a dedicated HTTPS endpoint, and snapshot / suspend-when-idle billing — none of which Managed Instances offers.
  • aws-lambda-web-adapter — the inverse (a web server on Lambda). Use it if your goal is the opposite direction.
  • RIE / serverless-offline / lambda-local — local emulation/testing, not a production MicroVM target.
  • Fargate / ECS — if you want a persistently-running service with standard load balancing and no 8-hour VM lifetime, a container is simpler.
  • A native MicroVM app — if you're greenfield on MicroVMs with no existing Lambda code, write directly to the platform; this adapter is dead weight.

Gotchas (learned from real runs)

  • Build role needs S3 read on your bucket. create-microvm-image fetches the build context as the BUILD_ROLE; if that role lacks s3:GetObject on BUCKET, the build fails with "Access denied when fetching artifact from S3."
  • Auth tokens expire at 60 min; VMs live up to 8 h. invoke.mjs caches and re-mints on 403 — a long-lived client that doesn't will start failing.
  • Concurrency differs from Lambda. Defaulted to 1 for safety (see ADAPTER_CONCURRENCY).
  • ARM64 only. Native deps must ship aarch64 binaries.

Requirements

  • Node.js ≥ 18 (uses global fetch, ESM).
  • AWS CLI v2 (lambda-microvms is v2-only) for build.sh / invoke.mjs control-plane calls.

Development

npm install       # pulls uvlink (used by the invoke client)
npm test          # NO AWS required

Tests spin up the adapter as a child process against fixtures and assert the full contract: ESM + CJS loading, both invoke modes, state persistence, all five hooks, error shapes, body cap, concurrency serialization, and the uvlink token-provider composition.

Files

| File | Role | |---|---| | runtime/bootstrap.mjs | The adapter — PID 1, HTTP↔Lambda bridge, 2 modes, lifecycle hooks. Zero deps. | | build.sh | S3 zip → wrapped build context → create-microvm-image. | | invoke.mjs | lambda.invoke()-shaped client (ensure-running → token → POST → response). | | example/index.mjs | A stateful sample handler; runs unchanged on Lambda or a MicroVM. | | explainer.html | A visual walkthrough of the whole approach. |

License & disclaimer

MIT © PandeyLabs — free for anyone to use, copy, modify, and distribute.

This is a personal hobby project, not affiliated with or endorsed by AWS, Lambda, or Amazon. It is provided "as is", without warranty of any kind and with no guarantee of future releases, maintenance, bug fixes, or support. Use at your own risk; you are responsible for anything you run with it (including AWS charges).