From 12002524ef54dc18e4c3004a505de4f71c9b8e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A9rault?= Date: Mon, 5 Oct 2026 11:46:39 +0300 Subject: [PATCH 1/6] docs: Fix outdated README sections on API keys, debugging and reloads - API keys: --apiKey was removed in v11, document how configured key values and the random key work, with a stage-based example - Debug logs: SLS_DEBUG no longer works, use --debug - Interactive debugging: node --inspect doesn't reach the framework process with the Serverless v4 binary, open the inspector through preLoadModules instead - Reloading: document that handlers are cached, reloadHandler, the offline:functionsUpdated lifecycle event, and that neither works in-process - Run modes: document the handler lifecycle, that callbackWaitsForEmptyEventLoop is ignored, and how Go handlers have to be configured Co-Authored-By: Claude Opus 5.5 --- README.md | 159 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 99 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 269bf1d5a..ca819a170 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ Default: '' #### reloadHandler -Reloads handler with each request. +Reloads handler with each request. Without it, handler instances are reused and keep running the code they loaded, until they are terminated (see [Handler lifecycle](#handler-lifecycle)). Has no effect with `useInProcess`. To reload only after code changes, see [Reloading handlers after code changes](#reloading-handlers-after-code-changes). #### rubyWatchDirs @@ -237,7 +237,8 @@ Turns on loading of your HTTP proxy settings from serverless.yml. #### terminateIdleLambdaTime -Number of seconds until an idle function is eligible for termination. +Number of seconds until an idle function is eligible for termination.
+Default: 60 #### useDocker @@ -264,7 +265,8 @@ Default: 3001 #### preLoadModules -Pre-load specified modules in the main thread to avoid crashes when importing in worker threads. Provide module names as a comma-separated list (e.g., "sharp,canvas").
+Pre-load specified modules in the main thread to avoid crashes when importing in worker threads. Provide module names as a comma-separated list (e.g., "sharp,canvas"). +Modules are resolved from `serverless-offline` itself, not from your service directory, so use package names or absolute paths (e.g. `${env:PWD}/my-module.js`).
Default: '' Any of the CLI options can be added to your `serverless.yml`. For example: @@ -327,6 +329,23 @@ NOTE: The Lambda handler process is running in a child process. +#### Go + +Without Docker, `go1.x` functions are built from source on each invocation, so Go needs to be installed and `handler` must point to the Go source file of the function, e.g. `hello/main.go`. + +A `handler` that points to a compiled binary, e.g. `bin/hello` as used for deployments, only works with `--useDocker`. The same applies to the `provided.al2023` and `provided.al2` runtimes, which AWS recommends for Go since `go1.x` is deprecated: run them with `--useDocker`, with the `bootstrap` binary in your service directory or in a layer. + +### Handler lifecycle + +Each invocation runs in an instance of the function (a worker thread, a child process or a Docker container, depending on the run mode): + +- an idle instance is reused for the next invocation of the same function, concurrent invocations each get a new instance. Unlike on AWS, there is no concurrency limit. +- with `reloadHandler`, every invocation gets a new instance. +- instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance can live up to twice as long. Your code is not notified when its instance is terminated, pending timers and open connections are dropped. +- in `in-process` mode, handler modules are loaded once and stay loaded until `serverless-offline` exits. All invocations share the same module state, and two invocations of the same function can run at the same time against that state, which never happens on AWS. + +`context.callbackWaitsForEmptyEventLoop` is ignored: the response is sent as soon as the handler's promise settles or the callback is called, even if there is still pending work in the event loop. Unlike on AWS, that pending work is not frozen and keeps running. + ## Invoke Lambda To use `Lambda.invoke` you need to set the lambda endpoint to the `serverless-offline` endpoint: @@ -402,27 +421,28 @@ that is used to call the function externally such as `aws-sdk` ``` serverless offline ... -offline: Starting Offline: local/us-east-1. -offline: Offline [http for lambda] listening on http://localhost:3002 -offline: Function names exposed for local invocation by aws-sdk: - * invokedHandler: myServiceName-dev-invokedHandler +Starting Offline at stage dev (us-east-1) + +Offline [http for lambda] listening on http://localhost:3002 +Function names exposed for local invocation by aws-sdk: + * invokedHandler: myServiceName-dev-invokedHandler ``` To list the available manual invocation paths exposed for targeting -by `aws-sdk` and `aws-cli`, use `SLS_DEBUG=*` with `serverless offline`. After the invoke server starts up, the full list of endpoints will be displayed: +by `aws-sdk` and `aws-cli`, run `serverless offline` with the `--debug` flag. After the invoke server starts up, the full list of endpoints will be displayed: ``` -SLS_DEBUG=* serverless offline +serverless offline --debug ... -offline: Starting Offline: local/us-east-1. +Starting Offline at stage dev (us-east-1) ... -offline: Offline [http for lambda] listening on http://localhost:3002 -offline: Function names exposed for local invocation by aws-sdk: - * invokedHandler: myServiceName-dev-invokedHandler -[offline] Lambda Invocation Routes (for AWS SDK or AWS CLI): - * POST http://localhost:3002/2015-03-31/functions/myServiceName-dev-invokedHandler/invocations -[offline] Lambda Async Invocation Routes (for AWS SDK or AWS CLI): - * POST http://localhost:3002/2014-11-13/functions/myServiceName-dev-invokedHandler/invoke-async/ +Offline [http for lambda] listening on http://localhost:3002 +Function names exposed for local invocation by aws-sdk: + * invokedHandler: myServiceName-dev-invokedHandler +s:sls:plugin:serverless-offline: Lambda Invocation Routes (for AWS SDK or AWS CLI): + * POST http://localhost:3002/2015-03-31/functions/myServiceName-dev-invokedHandler/invocations +s:sls:plugin:serverless-offline: Lambda Async Invocation Routes (for AWS SDK or AWS CLI): + * POST http://localhost:3002/2014-11-13/functions/myServiceName-dev-invokedHandler/invoke-async/ ``` You can manually target these endpoints with a REST client to debug your lambda @@ -517,11 +537,29 @@ Local layer contents are included in the cache key, so editing a ZIP or a file i ## Authorizers -### Token authorizers +### API keys -As defined in the [Serverless Documentation](https://serverless.com/framework/docs/providers/aws/events/apigateway/#setting-api-keys-for-your-rest-api) you can use API Keys as a simple authentication method. +As defined in the [Serverless Documentation](https://serverless.com/framework/docs/providers/aws/events/apigateway/#setting-api-keys-for-your-rest-api) you can use API Keys as a simple authentication method, by setting `private: true` on an `http` event. Requests to these endpoints need an `x-api-key` header with a valid key, otherwise they are answered with a 403. -Serverless-offline will emulate the behaviour of APIG and create a random token that's printed on the screen. With this token you can access your private methods by adding `x-api-key: generatedToken` to your request header. All API keys will share the same token. +The keys with a `value` in `provider.apiGateway.apiKeys` are valid keys. If none of the keys has a `value`, serverless-offline generates a random key on startup and prints it (`Key with token: ...`). Every valid key is accepted on every private endpoint. + +The `--apiKey` option was removed in v11. To use a fixed key for local development only, choose the keys by stage: + +```yml +custom: + apiKeys: + local: + - name: myKey + value: myLocalApiKeyForDevelopment + prod: + - name: myKey + +provider: + apiGateway: + apiKeys: ${self:custom.apiKeys.${sls:stage}} +``` + +and run `serverless offline start --stage local`. Use `--noAuth` to turn off the API key checks. ### Custom authorizers @@ -790,64 +828,43 @@ There's support for [websocketsApiRouteSelectionExpression](https://docs.aws.ama ## Debug process -The Serverless offline plugin will respond to the overall framework settings and output additional information to the console in debug mode. In order to do this you will have to set the `SLS_DEBUG` environment variable. You can run the following in the command line to switch to debug mode execution. - -> Unix: `export SLS_DEBUG=*` +### Debug logs -> Windows: `SET SLS_DEBUG=*` +Run `serverless offline` with the `--debug` flag to output additional information to the console, e.g. the Lambda invocation routes or why a request was rejected: -Interactive debugging is also possible for your project if you have installed the node-inspector module and Chrome browser. You can then run the following command line inside your project's root. +`serverless offline start --debug` -Initial installation: -`npm install -g node-inspector` +### Interactive debugging -For each debug run: -`node-debug sls offline` +Since Serverless Framework v4, the `serverless` command is a native binary which starts the Node.js process running the framework and its plugins. Running `node --inspect` on `serverless`, or setting `NODE_OPTIONS=--inspect`, therefore doesn't debug your handlers. -The system will start in wait status. This will also automatically start the Chrome browser and wait for you to set breakpoints for inspection. Set the breakpoints as needed and, then, click the play button for the debugging to continue. +Instead, open the inspector from within that process with the [`preLoadModules`](#preloadmodules) option. Create a file in your project: -Depending on the breakpoint, you may need to call the URL path for your function in a separate browser window for your serverless function to be run and made available for debugging. +```js +// start-debugger.mjs +import inspector from "node:inspector" -### Interactive Debugging with Visual Studio Code (VSC) +inspector.open() +``` -With newer versions of Node.js (6.3+) the node inspector is already part of your Node.js environment and you can take advantage of debugging inside your IDE with source-map support. Here is the example configuration to debug interactively with VSC. It has two steps. +and preload it, using its absolute path: -#### Step 1 : Adding a launch configuration in IDE +`serverless offline start --preLoadModules "$PWD/start-debugger.mjs"` -Add a new [launch configuration](https://code.visualstudio.com/docs/editor/debugging) to VSC like this: +The inspector listens on `127.0.0.1:9229`, and you can attach any Node.js debugger to it, e.g. `chrome://inspect` in Chrome, or this [launch configuration](https://code.visualstudio.com/docs/editor/debugging) in Visual Studio Code: ```json { - "cwd": "${workspaceFolder}", - "name": "Debug Serverless Offline", - "request": "launch", - "runtimeArgs": ["run", "debug"], - "runtimeExecutable": "npm", - "sourceMaps": true, + "name": "Attach to Serverless Offline", + "port": 9229, + "request": "attach", "type": "node" } ``` -#### Step 2: Adding a debug script - -You will also need to add a `debug` script reference in your `package.json` file - -Add this to the `scripts` section: +Handlers run in worker threads by default. Debuggers which support Node.js worker threads, such as Visual Studio Code, attach to them automatically. If yours doesn't, use [`useInProcess`](#useinprocess). -> Unix/Mac: `"debug" : "export SLS_DEBUG=* && node --inspect /usr/local/bin/serverless offline"` - -> Windows: `"debug": "SET SLS_DEBUG=* && node --inspect node_modules\\serverless\\bin\\serverless offline"` - -Example: - -```json -.... -"scripts": { - "debug" : "SET SLS_DEBUG=* && node --inspect node_modules\\serverless\\bin\\serverless offline" -} -``` - -In VSC, you can then add breakpoints to your code. To start a debug session you can either start your script in `package.json` by clicking the hovering debug IntelliSense icon or by going to your debug pane and selecting the Debug Serverless Offline configuration. +On Windows, pass the path as a file URL, e.g. `--preLoadModules "file:///C:/path/to/start-debugger.mjs"`. ## Resource permissions and AWS profile @@ -886,6 +903,28 @@ plugins: That works because all those plugins listen to the `offline:start:init` hook to do their processing. Similarly they listen to `offline:start:end` to perform cleanup (stop dynamo db, remove temporary files, etc). +### Reloading handlers after code changes + +Handler instances are reused between invocations (see [Handler lifecycle](#handler-lifecycle)), so they keep running the code they loaded. To pick up code changes, either: + +- use [`reloadHandler`](#reloadhandler), which runs every invocation in a new instance. Every request then pays the cost of loading your code. +- or invoke the `offline:functionsUpdated` lifecycle event after your code changed. It terminates all instances, so the next invocation of each function loads the new code. Bundler plugins and file watchers can invoke it, e.g. [serverless-offline-watcher](https://github.com/domdomegg/serverless-offline-watcher): + +```yml +plugins: + - serverless-offline + - serverless-offline-watcher + +custom: + serverless-offline-watcher: + - path: + - src/**/* + hook: + - offline:functionsUpdated +``` + +Neither works with `useInProcess`, as handler modules stay loaded until `serverless-offline` exits. + ## Credits and inspiration This plugin was initially a fork of [Nopik](https://github.com/Nopik/)'s [Serverless-serve](https://github.com/Nopik/serverless-serve). From 5cf803a4cd67d78e93434cef3922e254c3f8b49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A9rault?= Date: Mon, 5 Oct 2026 12:24:35 +0300 Subject: [PATCH 2/6] docs: Scope handler lifecycle to the runners it applies to - Go handlers without Docker and Java handlers aren't kept between invocations, so the reuse and termination rules don't apply to them - callbackWaitsForEmptyEventLoop is only ignored for Node.js handlers outside of Docker, the AWS runtime in the container honours it - Plain string API keys are currently accepted as key values (#1749) Co-Authored-By: Claude Opus 5.5 --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ca819a170..a6487eb83 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ Default: '' #### reloadHandler -Reloads handler with each request. Without it, handler instances are reused and keep running the code they loaded, until they are terminated (see [Handler lifecycle](#handler-lifecycle)). Has no effect with `useInProcess`. To reload only after code changes, see [Reloading handlers after code changes](#reloading-handlers-after-code-changes). +Reloads handler with each request. Without it, Node.js, Python, Ruby and Docker handler instances are reused and keep running the code they loaded, until they are terminated (see [Handler lifecycle](#handler-lifecycle)). Has no effect with `useInProcess`. To reload only after code changes, see [Reloading handlers after code changes](#reloading-handlers-after-code-changes). #### rubyWatchDirs @@ -337,14 +337,16 @@ A `handler` that points to a compiled binary, e.g. `bin/hello` as used for deplo ### Handler lifecycle -Each invocation runs in an instance of the function (a worker thread, a child process or a Docker container, depending on the run mode): +Node.js, Python and Ruby handlers, as well as all handlers run with `--useDocker`, run in an instance of the function (a worker thread, a child process or a Docker container, depending on the run mode): - an idle instance is reused for the next invocation of the same function, concurrent invocations each get a new instance. Unlike on AWS, there is no concurrency limit. - with `reloadHandler`, every invocation gets a new instance. - instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance can live up to twice as long. Your code is not notified when its instance is terminated, pending timers and open connections are dropped. - in `in-process` mode, handler modules are loaded once and stay loaded until `serverless-offline` exits. All invocations share the same module state, and two invocations of the same function can run at the same time against that state, which never happens on AWS. -`context.callbackWaitsForEmptyEventLoop` is ignored: the response is sent as soon as the handler's promise settles or the callback is called, even if there is still pending work in the event loop. Unlike on AWS, that pending work is not frozen and keeps running. +Without Docker, Go handlers are built and started in a new process for each invocation, so nothing is kept between invocations. Java handlers are invoked through `java-invoke-local`, either in a new process for each invocation, or through a `java-invoke-local --server` you started yourself, which `serverless-offline` doesn't manage. + +For Node.js handlers which don't run in Docker, `context.callbackWaitsForEmptyEventLoop` is ignored: the response is sent as soon as the handler's promise settles or the callback is called, even if there is still pending work in the event loop. Unlike on AWS, that pending work is not frozen and keeps running. ## Invoke Lambda @@ -541,7 +543,7 @@ Local layer contents are included in the cache key, so editing a ZIP or a file i As defined in the [Serverless Documentation](https://serverless.com/framework/docs/providers/aws/events/apigateway/#setting-api-keys-for-your-rest-api) you can use API Keys as a simple authentication method, by setting `private: true` on an `http` event. Requests to these endpoints need an `x-api-key` header with a valid key, otherwise they are answered with a 403. -The keys with a `value` in `provider.apiGateway.apiKeys` are valid keys. If none of the keys has a `value`, serverless-offline generates a random key on startup and prints it (`Key with token: ...`). Every valid key is accepted on every private endpoint. +The keys with a `value` in `provider.apiGateway.apiKeys` are valid keys. Keys given as a plain string, e.g. `- myKey`, are currently accepted as a key value as well, although Serverless uses the string as the key's name (see [#1749](https://github.com/dherault/serverless-offline/issues/1749)). If no key value is configured this way, serverless-offline generates a random key on startup and prints it (`Key with token: ...`). Every valid key is accepted on every private endpoint. The `--apiKey` option was removed in v11. To use a fixed key for local development only, choose the keys by stage: @@ -905,7 +907,7 @@ Similarly they listen to `offline:start:end` to perform cleanup (stop dynamo db, ### Reloading handlers after code changes -Handler instances are reused between invocations (see [Handler lifecycle](#handler-lifecycle)), so they keep running the code they loaded. To pick up code changes, either: +Node.js, Python, Ruby and Docker handler instances are reused between invocations (see [Handler lifecycle](#handler-lifecycle)), so they keep running the code they loaded. To pick up code changes, either: - use [`reloadHandler`](#reloadhandler), which runs every invocation in a new instance. Every request then pays the cost of loading your code. - or invoke the `offline:functionsUpdated` lifecycle event after your code changed. It terminates all instances, so the next invocation of each function loads the new code. Bundler plugins and file watchers can invoke it, e.g. [serverless-offline-watcher](https://github.com/domdomegg/serverless-offline-watcher): From 0abcb0723c8e8803e0b24316f5ce13f717d1d403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A9rault?= Date: Mon, 5 Oct 2026 12:30:06 +0300 Subject: [PATCH 3/6] docs: Mention usageIdentifierKey as an alternative to x-api-key Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6487eb83..c40613944 100644 --- a/README.md +++ b/README.md @@ -541,7 +541,7 @@ Local layer contents are included in the cache key, so editing a ZIP or a file i ### API keys -As defined in the [Serverless Documentation](https://serverless.com/framework/docs/providers/aws/events/apigateway/#setting-api-keys-for-your-rest-api) you can use API Keys as a simple authentication method, by setting `private: true` on an `http` event. Requests to these endpoints need an `x-api-key` header with a valid key, otherwise they are answered with a 403. +As defined in the [Serverless Documentation](https://serverless.com/framework/docs/providers/aws/events/apigateway/#setting-api-keys-for-your-rest-api) you can use API Keys as a simple authentication method, by setting `private: true` on an `http` event. Requests to these endpoints need an `x-api-key` header with a valid key, or a Lambda authorizer which returns a valid key as `usageIdentifierKey`, otherwise they are answered with a 403. The keys with a `value` in `provider.apiGateway.apiKeys` are valid keys. Keys given as a plain string, e.g. `- myKey`, are currently accepted as a key value as well, although Serverless uses the string as the key's name (see [#1749](https://github.com/dherault/serverless-offline/issues/1749)). If no key value is configured this way, serverless-offline generates a random key on startup and prints it (`Key with token: ...`). Every valid key is accepted on every private endpoint. From 2f1dfbe3584fe6c6cb08beaf952f0956ee1bef58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A9rault?= Date: Mon, 5 Oct 2026 12:35:33 +0300 Subject: [PATCH 4/6] docs: Describe idle termination timing as approximate Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c40613944..688e3de08 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,7 @@ Node.js, Python and Ruby handlers, as well as all handlers run with `--useDocker - an idle instance is reused for the next invocation of the same function, concurrent invocations each get a new instance. Unlike on AWS, there is no concurrency limit. - with `reloadHandler`, every invocation gets a new instance. -- instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance can live up to twice as long. Your code is not notified when its instance is terminated, pending timers and open connections are dropped. +- instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance usually lives one to two times that long, and longer while a previous cleanup (e.g. of a Docker container) is still in progress. Your code is not notified when its instance is terminated, pending timers and open connections are dropped. - in `in-process` mode, handler modules are loaded once and stay loaded until `serverless-offline` exits. All invocations share the same module state, and two invocations of the same function can run at the same time against that state, which never happens on AWS. Without Docker, Go handlers are built and started in a new process for each invocation, so nothing is kept between invocations. Java handlers are invoked through `java-invoke-local`, either in a new process for each invocation, or through a `java-invoke-local --server` you started yourself, which `serverless-offline` doesn't manage. From 2630c2973575f1a2b8ab383903b9fa1c6cc0de42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A9rault?= Date: Mon, 5 Oct 2026 12:41:15 +0300 Subject: [PATCH 5/6] docs: Fix the API keys TOC link and distinguish shutdown paths - The table of contents still linked to the renamed "Token authorizers" heading - Only worker threads are terminated abruptly, child processes get a SIGTERM and Docker containers are stopped with docker stop Co-Authored-By: Claude Opus 5.5 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 688e3de08..d62b55ae1 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ This plugin is updated by its users, I just do maintenance and ensure that PRs a - [The `process.env.IS_OFFLINE` variable](#the-processenvis_offline-variable) - [Docker and Layers](#docker-and-layers) - [Authorizers](#authorizers) - - [Token authorizers](#token-authorizers) + - [API keys](#api-keys) - [Custom authorizers](#custom-authorizers) - [Remote authorizers](#remote-authorizers) - [JWT authorizers](#jwt-authorizers) @@ -341,7 +341,7 @@ Node.js, Python and Ruby handlers, as well as all handlers run with `--useDocker - an idle instance is reused for the next invocation of the same function, concurrent invocations each get a new instance. Unlike on AWS, there is no concurrency limit. - with `reloadHandler`, every invocation gets a new instance. -- instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance usually lives one to two times that long, and longer while a previous cleanup (e.g. of a Docker container) is still in progress. Your code is not notified when its instance is terminated, pending timers and open connections are dropped. +- instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance usually lives one to two times that long, and longer while a previous cleanup (e.g. of a Docker container) is still in progress. Worker threads are terminated abruptly, without notifying your code. Python and Ruby processes receive a `SIGTERM`, and Docker containers are stopped with `docker stop`, so they can react to it. Either way, pending work such as timers and open connections is not guaranteed to finish. - in `in-process` mode, handler modules are loaded once and stay loaded until `serverless-offline` exits. All invocations share the same module state, and two invocations of the same function can run at the same time against that state, which never happens on AWS. Without Docker, Go handlers are built and started in a new process for each invocation, so nothing is kept between invocations. Java handlers are invoked through `java-invoke-local`, either in a new process for each invocation, or through a `java-invoke-local --server` you started yourself, which `serverless-offline` doesn't manage. From c65db7b4a36173bc379e38959d7812e017f3b704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A9rault?= Date: Mon, 5 Oct 2026 12:48:21 +0300 Subject: [PATCH 6/6] docs: Qualify SIGTERM delivery for Python handlers PythonRunner starts the interpreter with shell: true, so kill() may only reach the shell. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d62b55ae1..9d12c6cf2 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,7 @@ Node.js, Python and Ruby handlers, as well as all handlers run with `--useDocker - an idle instance is reused for the next invocation of the same function, concurrent invocations each get a new instance. Unlike on AWS, there is no concurrency limit. - with `reloadHandler`, every invocation gets a new instance. -- instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance usually lives one to two times that long, and longer while a previous cleanup (e.g. of a Docker container) is still in progress. Worker threads are terminated abruptly, without notifying your code. Python and Ruby processes receive a `SIGTERM`, and Docker containers are stopped with `docker stop`, so they can react to it. Either way, pending work such as timers and open connections is not guaranteed to finish. +- instances which are idle for `terminateIdleLambdaTime` seconds are terminated. The check runs every `terminateIdleLambdaTime` seconds, so an idle instance usually lives one to two times that long, and longer while a previous cleanup (e.g. of a Docker container) is still in progress. Worker threads are terminated abruptly, without notifying your code. Ruby processes receive a `SIGTERM`, and Docker containers are stopped with `docker stop`, so they can react to it. Python processes are sent a `SIGTERM` as well, but since they are started through a shell, the signal may only reach the shell and not the Python interpreter. Either way, pending work such as timers and open connections is not guaranteed to finish. - in `in-process` mode, handler modules are loaded once and stay loaded until `serverless-offline` exits. All invocations share the same module state, and two invocations of the same function can run at the same time against that state, which never happens on AWS. Without Docker, Go handlers are built and started in a new process for each invocation, so nothing is kept between invocations. Java handlers are invoked through `java-invoke-local`, either in a new process for each invocation, or through a `java-invoke-local --server` you started yourself, which `serverless-offline` doesn't manage.