LocalStack LogoLocalStack Icon

Debug AWS Lambda Functions Locally Using LocalStack’s Lambda Debug Mode

Learn how to debug AWS Lambda functions locally using LocalStack’s Lambda Debug Mode with lifted timeouts, hot reload, and full step-through debugging in VS Code.

Debug AWS Lambda Functions Locally Using LocalStack’s Lambda Debug Mode

Introduction

Debugging AWS Lambda functions locally is difficult due to the stateless, event-driven nature of serverless environments and their short execution timeouts. LocalStack’s Lambda Debug Mode changes that by enabling full remote debugging with lifted timeouts, allowing developers to inspect their code step-by-step using familiar tools like VS Code.

In this tutorial, we’ll walk through three common use cases for debugging Lambda functions locally:

  • Debug a Lambda function locally
  • Debug a Lambda function with multiple concurrent invocations
  • Debug two Lambda functions locally

Key Concepts

Lambda Debug Mode simplifies the process of debugging AWS Lambda functions locally.

The process involves two main parts: first, enabling the mode globally with the LAMBDA_DEBUG_MODE=1 flag (which automatically extends timeouts), and second, configuring specific debugger settings (like unique ports) for each function you want to debug, typically using a YAML configuration file.

This file allows you to automate configuration steps that previously required manual setup. It extends timeouts, enables debugger attachment, and supports per-function configuration for debugger attachment.

Enable Debug Mode

Start LocalStack with the LAMBDA_DEBUG_MODE environment variable set to 1:

Terminal window
LAMBDA_DEBUG_MODE=1 localstack start

This turns on debug mode globally and updates timeouts for debugging:

  • Container startup timeout: Increased to allow debugger connection before failure.
  • Lambda execution timeout: Extended to avoid ending long debug sessions early.
  • API Gateway integration timeout: Synced with Lambda timeout to prevent errors.

Debug Port Configuration

To control debugging per function or set custom ports, use the Lambda Debug Mode config file (lambda_debug_mode_config.yaml). It replaces older methods like setting LAMBDA_DOCKER_FLAGS and supports organized, per-function settings. Here’s an example of the config file:

functions:
- arn: arn:aws:lambda:us-east-1:000000000000:function:my-lambda
debug-port: 5678

Each entry in the configuration file must use the full Lambda ARN, as the debugger settings are linked directly to that specific resource identifier. This file enables per-function debugger settings.

Importantly, this configuration is dynamic: you can modify the file (adding/removing functions or changing ports) while LocalStack is running, and the changes will be applied automatically without requiring a restart.

Mount the Config

To use the configuration file, you need to mount it into the LocalStack container and tell LocalStack where to find it using environment variables and volume mount:

Terminal window
LOCALSTACK_LAMBDA_DEBUG_MODE=1 \
LOCALSTACK_LAMBDA_DEBUG_MODE_CONFIG_PATH=/tmp/lambda_debug_mode_config.yaml \
localstack start --volume /path/to/your/debug-config.yaml:/tmp/lambda_debug_mode_config.yaml

Note: Replace /path/to/your/debug-config.yaml with the actual path on your host machine.

Hot Reload setup

While debugging the Lambda functions, it is recommended to use LocalStack’s special Hot Reload setup. This works by specifying hot-reload as a special S3 bucket name in the --code parameter, instructing LocalStack to mount the local directory (specified by S3Key) into the Lambda container.

Terminal window
awslocal lambda create-function \
...
--code "S3Bucket=hot-reload,S3Key=$(pwd)/" \ # Mounts the current directory
...

Code changes are then immediately reflected, eliminating the need for redeploying the Lambda function after every change.

Notes

  • Set a unique debug-port for each function in the config file to prevent port conflicts and allow parallel debugging.
  • LocalStack auto-detects config file changes, so you can update ports or settings without restarting the container.
  • Lambda containers in debug mode stop automatically after the session ends, freeing system resources.

Debug a Single Lambda function

In this beginner scenario, we’ll walk through debugging a Python Lambda function locally using LocalStack’s Lambda Debug Mode. We’ll deploy a Lambda function with a one-second timeout—which is automatically lifted when Debug Mode is enabled.

1.1: Create Lambda function

Start by creating a new file named handler.py and adding the following Lambda handler function:

def handler(event, context):
# Wait for the debugger to get attached.
wait_for_debug_client()
# Print the incoming invocation event.
print(event)
# Return the incoming invocation event.
return event

The wait_for_debug_client() function initializes a debug server using debugpy. It allows time for a debugger (e.g., Visual Studio Code) to connect, and cancels the wait if no client attaches.

Here’s the full implementation:

def wait_for_debug_client(timeout=3600):
"""Utility function to enable debugging with Visual Studio Code"""
import time, threading
import sys, glob
sys.path.append(glob.glob(".venv/lib/python*/site-packages")[0])
import debugpy
debugpy.listen(("0.0.0.0", 19891))
class T(threading.Thread):
daemon = True
def run(self):
time.sleep(timeout)
print("Canceling debug wait task ...")
debugpy.wait_for_client.cancel()
T().start()
print("Waiting for client to attach debugger ...")
debugpy.wait_for_client()
if __name__ == "__main__":
handler({}, {})

The full source file is also available in the repository.

1.2 Configure Lambda Debug Mode

Next, create a file named lambda_debug_mode_config.yaml to configure debugging for your function:

functions:
arn:aws:lambda:us-east-1:000000000000:function:function-one:
debug-port: 19891

This maps the function function-one to port 19891, which your debugger will connect to.

Start LocalStack with Lambda Debug Mode enabled and mount the configuration file into the container:

Terminal window
LOCALSTACK_LAMBDA_DEBUG_MODE=1 \
LOCALSTACK_LAMBDA_DEBUG_MODE_CONFIG_PATH=/tmp/lambda_debug_mode_config.yaml \
localstack start --volume $PWD/lambda_debug_mode_config.yaml:/tmp/lambda_debug_mode_config.yaml

1.3: Set Up VS Code Debug Configuration

Now configure VS Code to attach to the Lambda function at runtime. In your project, create a .vscode directory and add a file named launch.json with the following content:

{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Remote Attach",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 19891
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
}
]
}

After deploying and invoking the Lambda function, you’ll use this configuration to connect the debugger.

1.4: Deploy Lambda function

Install the required dependencies for your function—specifically debugpy—using virtualenv or your preferred Python environment manager:

Terminal window
virtualenv .venv
source .venv/bin/activate
pip3 install debugpy

Now deploy the Lambda function using awslocal, a CLI wrapper over the AWS CLI:

Terminal window
awslocal lambda create-function \
--function-name function-one \
--timeout 1 \
--code "S3Bucket=hot-reload,S3Key=$(pwd)/" \
--handler handler.handler \
--role arn:aws:iam::000000000000:role/test-role \
--runtime python3.12

Note: We are intentionally setting a short timeout of 1 second (--timeout 1) to show that Lambda Debug Mode overrides it and provides enough time for debugging.

This example uses LocalStack’s special Hot Reload setup, as described earlier. Wait for the function to become active:

Terminal window
awslocal lambda wait function-active-v2 --function-name function-one

1.5: Start debugging

Once the Lambda is deployed, invoke it with an extended timeout to start debugging:

Terminal window
AWS_MAX_ATTEMPTS=1 \
awslocal lambda invoke \
--cli-connect-timeout 3600 \
--cli-read-timeout 3600 \
--cli-binary-format raw-in-base64-out \
--function-name function-one \
--payload '{"message": "Testing Lambda Debug Mode lifting the 1-second timeout for function-one."}' \
/dev/stdout 2>/dev/stderr

Note: The --cli-connect-timeout and --cli-read-timeout flags raise AWS CLI timeouts to prevent awslocal from timing out during debugging.

In Visual Studio Code:

  1. Open handler.py.
  2. Set a breakpoint (e.g., on print(event)).
  3. Go to the Run and Debug view.
  4. Select the Python: Remote Attach configuration.
  5. The remote debugger will attach, and you can step through the code.

Remote debugger in action on VS Code with LocalStack Lambda Debug Mode

1.6: Make changes and reload

Thanks to Hot Reload, any updates to your Lambda function will take effect immediately without redeployment. For example:

def handler(event, context):
wait_for_debug_client()
print(event)
print("!! Additional log output !!") # New line
return event

On your next invocation, the new log message will appear in the Lambda output.

This enables a fast and flexible debug loop during development.

Debug a Lambda function with multiple concurrent invocations

In this scenario, we’ll explore how Lambda Debug Mode handles multiple invocations of the same Lambda function occurring around the same time. We’ll reuse the same function-one Lambda created in Scenario 1, which includes a one-second timeout that is automatically lifted by Debug Mode.

The goal is to observe how LocalStack behaves when the Lambda function is invoked several times in quick succession while a debugger is attached.

2.1: Invoke Lambda function multiple times

To simulate this, run the following script. It invokes the Lambda function three times, spaced five seconds apart:

Terminal window
for i in {1..3}; do
echo "Invoking the Lambda function, attempt $i."
AWS_MAX_ATTEMPTS=1 \
awslocal lambda invoke \
--cli-connect-timeout 3600 \
--cli-read-timeout 3600 \
--cli-binary-format raw-in-base64-out \
--function-name "$FUNCTION_NAME" \
--payload "{\"message\": \"Testing Lambda Debug Mode lifting the 1-second timeout for function-one. Attempt $i.\"}" \
/dev/stdout 2>/dev/stderr &
sleep 5
done

Each invocation uses a slightly different payload so you can distinguish them in the logs.

2.2: Debug the first invocation

While the function is being invoked, open Visual Studio Code and:

  1. Set a breakpoint in handler.py.
  2. Open the Run and Debug view.
  3. Select the Python: Remote Attach configuration.
  4. Start the debugger.

Lambda Debug Mode removes the timeout and pauses execution, giving you time to inspect the logic in your function.

What happens to other invocations?

Only the first invocation will be handled. Lambda Debug Mode enforces a limit of one active debug session per function. The remaining invocations will be rejected, and the LocalStack logs will show messages like the following:

2025-02-04T18:13:45.327 WARN --- [et.reactor-0] l.s.l.i.counting_service : Concurrent lambda invocations disabled for 'arn:aws:lambda:us-east-1:000000000000:function:function-one:$LATEST' by Lambda Debug Mode
2025-02-04T18:13:45.333 INFO --- [et.reactor-0] localstack.request.aws : AWS lambda.Invoke => 429 (TooManyRequestsException)
2025-02-04T18:13:50.302 WARN --- [et.reactor-0] l.s.l.i.counting_service : Concurrent lambda invocations disabled for 'arn:aws:lambda:us-east-1:000000000000:function:function-one:$LATEST' by Lambda Debug Mode
2025-02-04T18:13:50.303 INFO --- [et.reactor-0] localstack.request.aws : AWS lambda.Invoke => 429 (TooManyRequestsException)

Debug two Python Lambda functions

Real-world serverless applications often consist of multiple Lambda functions. This final scenario demonstrates how Lambda Debug Mode allows you to debug several functions concurrently during local development. We’ll configure two Python Lambda functions locally, each assigned a unique debug port.

This setup enables you to attach separate debuggers and step through the code of different functions active at the same time. LocalStack automatically lifts timeouts for both and configures their containers for debugging.

Let’s build on the setup from Scenario 1 and adapt it for a second Lambda function.

3.1: Create second Lambda function

Create a new file named handler_2.py and add the following Lambda handler:

def handler(event, context):
# Wait for the debugger to get attached.
wait_for_debug_client()
# Print a message to log that this is handler_2.py.
print("The handler of handler_2.py is evaluating.")
# Print the incoming invocation event.
print(event)
# Return the incoming invocation event.
return event
def wait_for_debug_client(timeout=3600):
"""Utility function to enable debugging with Visual Studio Code"""
import time, threading
import sys, glob
sys.path.append(glob.glob(".venv/lib/python*/site-packages")[0])
import debugpy
debugpy.listen(("0.0.0.0", 19892))
class T(threading.Thread):
daemon = True
def run(self):
time.sleep(timeout)
print("Canceling debug wait task ...")
debugpy.wait_for_client.cancel()
T().start()
print("Waiting for client to attach debugger ...")
debugpy.wait_for_client()
if __name__ == "__main__":
handler({}, {})

This version mirrors the debug logic used in handler.py, with a different port (19892) for the second Lambda function.

The full source file is also available in the repository.

3.2: Update Lambda Debug Mode configuration

Open lambda_debug_mode_config.yaml and update it to define a new debug port for the second function:

functions:
arn:aws:lambda:us-east-1:000000000000:function:function-one:
debug-port: 19891
arn:aws:lambda:us-east-1:000000000000:function:function-two:
debug-port: 19892

LocalStack detects configuration file changes automatically—no restart required.

3.3: Add second debug configuration in VS Code

Update .vscode/launch.json to include a second configuration for function-two:

{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Remote Attach to function_one",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 19891
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
},
{
"name": "Python: Remote Attach to function_two",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 19892
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
}
]
}

3.4: Deploy second Lambda function

Use awslocal to create the second function, pointing to the new handler file:

Terminal window
awslocal lambda create-function \
--function-name function-two \
--timeout 1 \
--code "S3Bucket=hot-reload,S3Key=$(pwd)/" \
--handler handler_2.handler \
--role arn:aws:iam::000000000000:role/test-role \
--runtime python3.12

Wait until the function is active:

Terminal window
awslocal lambda wait function-active-v2 --function-name function-two

3.5: Debug second Lambda function

Invoke the second function and attach your debugger:

Terminal window
AWS_MAX_ATTEMPTS=1 \
awslocal lambda invoke \
--cli-connect-timeout 3600 \
--cli-read-timeout 3600 \
--cli-binary-format raw-in-base64-out \
--function-name function-two \
--payload '{"message": "Testing Lambda Debug Mode lifting the 1-second timeout for function-two."}' \
/dev/stdout 2>/dev/stderr

Then in Visual Studio Code:

  1. Open handler_2.py.
  2. Set a breakpoint (e.g., on print(event)).
  3. Go to the Run and Debug view.
  4. Select Python: Remote Attach to function_two.
  5. Begin debugging.

Remote debugger in action on VS Code with LocalStack Lambda Debug Mode for function_two

Debugging both functions simultaneously

You can also run both Lambda functions with their respective debuggers at the same time. Each function listens on a different port, and Lambda Debug Mode lifts the timeout for both, allowing simultaneous debugging sessions without conflict.

Conclusion

LocalStack’s Lambda Debug Mode simplifies local debugging for serverless workflows, offering:

  • Lifted timeouts for uninterrupted debugging sessions
  • Remote debugger support for single and chained Lambda functions
  • Full support for event-driven triggers like SQS

Whether you’re working on simple functions or more complex event chains, you can now debug your logic locally before deploying to the cloud. That reduces friction in your development cycles, helps catch errors early, and improves confidence in your serverless applications.


Harsh Mishra
Harsh Mishra
Engineer at LocalStack
Harsh Mishra is an Engineer at LocalStack and AWS Community Builder. Harsh has previously worked at HackerRank, Red Hat, and Quansight, and specialized in DevOps, Platform Engineering, and CI/CD pipelines.
Marco Edoardo Palma
Marco Edoardo Palma
Software Engineer at LocalStack
Marco is a Software Developer at LocalStack and is pursuing a PhD at the Software Evolution and Architecture Lab, University of Zurich.
Quetzalli Writes
Quetzalli Writes
Developer Educator at LocalStack
Quetzalli lives for docs, information architecture, technical storytelling, and fixing problems at LocalStack. She has 10+ years in tech and is the published author of "Docs-as-Ecosystem: The Community Approach to Engineering"