Open-source project
vercel/fun avatar
vercel/fun

fun: a local Lambda runtime for developing serverless functions

ƒun - Local serverless function λ development runtime

1,021 stars44 forksTypeScriptApache-2.0

At a glance

What is it?
fun, published as `@vercel/fun`, starts a local server that hosts an AWS Lambda function so you can invoke it from your own code without deploying anything. Its API is three calls, create the function, call it, destroy it, and its caveat list is the most valuable part of the README, because it explains exactly how the local environment differs from the real one.
Who is it for?
Adopt fun if you develop AWS Lambda functions in Node or Python and you want a fast local loop that runs the real handler code with the real event and context objects, rather than a mock. Do not adopt it as a security boundary, because the README states that the processes are not sandboxed and not chrooted and run as your own user, so the isolation Lambda gives you in production does not exist locally.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three calls, and a function you can destroy

The example in the README starts from an ordinary Lambda handler and then shows the whole client surface. The handler is the Node style `exports.handler` taking an event, a context and a callback, and the client side calls `createFunction` with a configuration object, receives a function handle, invokes that handle with a payload, and finally calls `fn.destroy()`.

js
import { createFunction } from '@vercel/fun';

async function main() {
  const fn = await createFunction({
    Code: {
      Directory: __dirname + '/example'
    },
    Handler: 'index.handler',
    Runtime: 'nodejs8.10',
    Environment: {
      Variables: {
        HELLO: 'world'
      }
    },
    MemorySize: 512
  });

  const res = await fn({ hello: 'world' });
  console.log(res);

  await fn.destroy();
}

The configuration mirrors the shape of the AWS API, with the code location, the handler name, the runtime, the environment variables and the memory size, and the README notes that `ZipFile` works too, or that an already unzipped directory may be specified. The invocation is a plain function call, and the README explains that a new instance of the function is initialised if there is not an available one ready to process, which is the pool behaviour you would want. The `destroy()` call is the part with a purpose, since it destroys the function so that the processes are cleaned up and the API server is shut down, which the README calls useful for hot reloading.

The caveats section is the documentation that matters

The README says that fun provides an execution environment that closely resembles the real Lambda environment, with some key differences that are documented, and then lists them. Lambda processes run as your own user, not as the `sbx_user1051` user. Processes are not sandboxed and not chrooted, so you should not rely on hard coded locations such as `/var/task`, `/var/runtime` or `/opt`, and instead your function code should use the environment variables that represent those locations, namely `LAMBDA_TASK_ROOT` and `LAMBDA_RUNTIME_DIR`. Processes are frozen by sending the `SIGSTOP` signal to the Lambda process and unfrozen with `SIGCONT`, rather than using the cgroup freezer. And Lambdas that compile to native executables, in other words Go, need to be compiled for your operating system, so on macOS the binary must be executable on macOS. Each of those is a small thing, and together they are the difference between a local environment that teaches you the wrong lesson and one that does not.

A long runtime list, and the binaries it downloads

fun aims to support all the runtimes that AWS Lambda provides, and the README lists what is currently implemented. The Node.js entries range from the plain `nodejs`, which uses the system `node` binary, through `nodejs6.10`, `nodejs8.10`, `nodejs10.x`, `nodejs12.x` and `nodejs14.x`, each of which uses a downloaded binary, with the specific versions named, among them v6.10.0, v8.10.0, v10.15.3, v12.22.7 and v14.18.1. The Python side follows the same pattern, with `python` and `python3` using the system interpreter, the latter falling back to `python`, plus `python2.7`, `python3.6` and `python3.7` using downloaded binaries at v2.7.12, v3.6.8 and v3.7.2. Then there is `go1.x` for functions written in Go, where the binary must be compiled for your platform, `provided` for custom runtimes, and `executable` for executables powered by Fluid compute. The distinction between the system runtime and the downloaded one is the interesting detail, because the downloaded runtimes are the ones that let you match the version your function targets, and they are also a set of binaries fetched onto your machine at first use.

What the package is actually made of

The manifest shows a small dependency set with a few telling members. The API server is `micro`, pinned to a canary build, which is what the handle returned by `createFunction` invokes, and `generic-pool` is the pool behind the behaviour of reusing a warm instance and starting a new one when none is free. The rest is plumbing: `async-listen` for the server socket, `debug` for logging, `tar` and `yauzl-promise` for handling zipped function packages, `tinyexec` for running the function process, `tree-kill` for cleaning up process trees, `uid-promise` for the user handling that the caveats mention, `xdg-app-paths` for locating data directories, `semver` for runtime version comparisons, and `path-to-regexp` for routing. The package is published as `@vercel/fun` with the version left at 0.0.0-development, and the `release.config.js` file and `RELEASING.md` in the root show that the published version is set by the release tooling rather than by editing the manifest.

The build and the test rig

The build script is short and does something slightly unusual, removing the dist directory, running the TypeScript compiler and then copying the non-TypeScript files across with rsync, which is how non-TypeScript sources and tests reach the build output. Only `dist/src` is published, and the `prepublishOnly` script rebuilds and removes the test directory from the output, so the tests are never shipped. Tests run with vitest, and the `test` script prints the Node version first, then builds, then runs the suite, with a separate coverage path through nyc and codecov for the text-lcov report. Linting is prettier with a single quote style over the TypeScript sources, applied through lint-staged and a pre-commit hook. The repository layout is small, with `src/`, `test/`, an `example/` directory containing a dump example, a `util/` directory, a vitest configuration, a tsconfig and the pnpm lockfile, and the CI workflow is the Node CI badge in the README.

Where it fits in a development loop, and where it does not

The design puts fun squarely in the local development phase. The handle it returns behaves like a remote call, the function can be created, invoked many times with different payloads and destroyed, and the README's mention of hot reloading explains the shutdown behaviour. What it is not is a local Lambda emulator in the sense of a compatibility guarantee, because the README is explicit that the environment only closely resembles the real one and that the differences are documented. The security consequence is the one to internalise, since a Lambda that assumes it is running as an unprivileged sandbox user inside a chroot will find that locally it is running as your account with your filesystem. The second consequence is the freezing mechanism, where the local implementation uses signals rather than the cgroup freezer, so a piece of code that depends on the freezer's semantics will behave differently. For a function that reads a file, calls an API and returns JSON, which is most of them, the tool is a good fit.

Editorial conclusion

Adopt fun if you develop AWS Lambda functions in Node or Python and you want a fast local loop that runs the real handler code with the real event and context objects, rather than a mock. Do not adopt it as a security boundary, because the README states that the processes are not sandboxed and not chrooted and run as your own user, so the isolation Lambda gives you in production does not exist locally. Before you rely on it, read the caveat list and change any hard coded paths such as `/var/task` to the `LAMBDA_TASK_ROOT` and `LAMBDA_RUNTIME_DIR` environment variables, and if your function is written in Go, compile the binary for the machine you are on. The licence is Apache-2.0, the current release is 1.3.6 from 2026-09-29, and the last push was the same day.

Frequently asked questions

How do I run a Lambda function locally with fun?

Call `createFunction` with the code location, handler name, runtime, environment variables and memory size, then invoke the returned handle with a payload, and call `fn.destroy()` when you are done so the processes are cleaned up and the API server shuts down, which the README says is useful for hot reloading.

What are the differences between fun and the real Lambda environment?

Lambda processes run as your own user rather than the `sbx_user1051` user, they are not sandboxed nor chrooted, they are frozen with the `SIGSTOP` signal and unfrozen with `SIGCONT` rather than the cgroup freezer, and native executables such as Go binaries have to be compiled for your operating system.

Why should my function avoid /var/task and /var/runtime?

Because the processes are not chrooted locally, so those paths do not exist as they do in Lambda. The README says to use the environment variables that represent those locations instead, namely `LAMBDA_TASK_ROOT` and `LAMBDA_RUNTIME_DIR`.

Which runtimes does fun support?

The README lists `nodejs` and `python` and `python3` using system binaries, downloaded versions of Node 6.10, 8.10, 10.x, 12.x and 14.x, downloaded versions of Python 2.7, 3.6 and 3.7, `go1.x` for functions compiled for your platform, `provided` for custom runtimes, and `executable` for executables powered by Fluid compute.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. vercel/fun on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vercel-fun.svg)](https://hysenlabs.com/projects/vercel-fun)

Was this useful?