Local Development
Introduction
Section titled “Introduction”In this quickstart you’ll start LocalStack and deploy a simple serverless API including a Lambda function backed by DynamoDB, entirely on your local machine. No AWS account needed.
By the end you will have:
- LocalStack running locally in Docker
- A Lambda function deployed and invokable via a public URL
- A DynamoDB table storing data written by the Lambda
- A local environment that behaves like real AWS
Choose your preferred deployment style below: AWS CLI or Terraform.
Prerequisites
Section titled “Prerequisites”- Docker installed and running
- A LocalStack account
Step 1 — Install and start LocalStack
Section titled “Step 1 — Install and start LocalStack”The fastest way to get LocalStack running locally is with lstk, a lightweight CLI that handles authentication and image setup automatically.
Install lstk:
brew install localstack/tap/lstk # macOS / Linux with Homebrewnpm install -g @localstack/lstk # or via npmThen start LocalStack:
lstkOn first run, lstk opens a browser window to authenticate. It also pulls the LocalStack image and starts the container automatically.
When the container is ready, you’ll see log lines like:
✔︎ LocalStack ready (containerId: 400b3e61f3c6)• Endpoint: localhost.localstack.cloud:4566• Web app: https://app.localstack.cloud> Tip: View deployed resources: lstk statusIf you prefer the full-featured LocalStack CLI, install it first, then configure your auth token, and start LocalStack:
localstack startWhen the container is ready, you’ll see log lines like:
__ _______ __ __ / / ____ _________ _/ / ___// /_____ ______/ /__ / / / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/ / /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,</_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_|
💻 LocalStack CLI ${LOCALSTACK_VERSION}👤 Profile: default
[12:47:13] starting LocalStack in Docker mode 🐳 localstack.py:494 preparing environment bootstrap.py:1240 configuring container bootstrap.py:1248 starting container bootstrap.py:1258[12:47:15] detaching bootstrap.py:1262Step 2 — Deploy the serverless API
Section titled “Step 2 — Deploy the serverless API”Now deploy a Lambda function and a DynamoDB table. For this tutorial, you can choose between creating resources using the AWS CLI via our awslocal wrapper or Terraform via our tflocal wrapper.
-
Install the
awslocalwrapper if you haven’t already:Terminal window pip install awscli-local -
Create the Lambda function
The below code creates a Lambda function using Python and zips the code to be ready for deployment.
Terminal window mkdir -p /tmp/localstack-democat > /tmp/localstack-demo/handler.py << 'EOF'import json, boto3, os, uuiddef handler(event, context):table = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME'])method = event.get('requestContext', {}).get('http', {}).get('method', 'GET')if method == 'POST':item = {'id': str(uuid.uuid4()), **json.loads(event.get('body', '{}'))}table.put_item(Item=item)return {'statusCode': 200, 'body': json.dumps(item)}result = table.scan()return {'statusCode': 200, 'body': json.dumps(result['Items'])}EOFcd /tmp/localstack-demo && zip handler.zip handler.py -
Create the DynamoDB table
Terminal window awslocal dynamodb create-table \--table-name Messages \--attribute-definitions AttributeName=id,AttributeType=S \--key-schema AttributeName=id,KeyType=HASH \--billing-mode PAY_PER_REQUEST -
Deploy the Lambda
Terminal window awslocal lambda create-function \--function-name messages-api \--runtime python3.12 \--handler handler.handler \--zip-file fileb:///tmp/localstack-demo/handler.zip \--role arn:aws:iam::000000000000:role/lambda-role \--environment Variables={TABLE_NAME=Messages}awslocal lambda wait function-active --function-name messages-api -
Create a public function URL
Terminal window awslocal lambda create-function-url-config \--function-name messages-api \--auth-type NONE -
Retrieve the URL
Terminal window LAMBDA_URL=$(awslocal lambda list-function-url-configs \--function-name messages-api \--query 'FunctionUrlConfigs[0].FunctionUrl' \--output text)echo $LAMBDA_URL
-
Install Terraform and the
tflocalwrapper:Terminal window brew install hashicorp/tap/terraformpip install terraform-localFor additional installation options, see the Terraform installation guide.
-
Create a project directory.
Terminal window mkdir -p /tmp/localstack-demo && cd /tmp/localstack-demoThen create a
main.tffile with the following content:main.tf terraform {required_providers {aws = { source = "hashicorp/aws" }archive = { source = "hashicorp/archive" }}}resource "aws_dynamodb_table" "messages" {name = "Messages"billing_mode = "PAY_PER_REQUEST"hash_key = "id"attribute {name = "id"type = "S"}}data "archive_file" "lambda" {type = "zip"output_path = "${path.module}/handler.zip"source {filename = "handler.py"content = <<-EOFimport json, boto3, os, uuiddef handler(event, context):table = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME'])method = event.get('requestContext', {}).get('http', {}).get('method', 'GET')if method == 'POST':item = {'id': str(uuid.uuid4()), **json.loads(event.get('body', '{}'))}table.put_item(Item=item)return {'statusCode': 200, 'body': json.dumps(item)}result = table.scan()return {'statusCode': 200, 'body': json.dumps(result['Items'])}EOF}}resource "aws_iam_role" "lambda_role" {name = "lambda-role"assume_role_policy = jsonencode({Version = "2012-10-17"Statement = [{ Action = "sts:AssumeRole", Effect = "Allow",Principal = { Service = "lambda.amazonaws.com" } }]})}resource "aws_lambda_function" "messages_api" {function_name = "messages-api"runtime = "python3.12"handler = "handler.handler"filename = data.archive_file.lambda.output_pathsource_code_hash = data.archive_file.lambda.output_base64sha256role = aws_iam_role.lambda_role.arnenvironment {variables = { TABLE_NAME = aws_dynamodb_table.messages.name }}}resource "aws_lambda_function_url" "messages_api" {function_name = aws_lambda_function.messages_api.function_nameauthorization_type = "NONE"}output "function_url" {value = aws_lambda_function_url.messages_api.function_url} -
Deploy
Terminal window tflocal inittflocal apply -auto-approve -
Retrieve the URL
Terminal window LAMBDA_URL=$(tflocal output -raw function_url)echo $LAMBDA_URL
Step 3 — Test the API
Section titled “Step 3 — Test the API”Store a message:
curl -X POST "$LAMBDA_URL" \ -H "Content-Type: application/json" \ -d '{"message": "Hello, LocalStack!"}'You should get back a response like:
{ "id": "a1b2c3d4-...", "message": "Hello, LocalStack!" }List all messages:
curl "$LAMBDA_URL"That’s the win. You just invoked a real Lambda function that wrote to a real DynamoDB table — all running locally, with no AWS account and no cloud costs.
Step 4 — Inspect your resources
Section titled “Step 4 — Inspect your resources”You can browse the resources you just deployed in the LocalStack Web Application. Navigate to your Default Instance and click through to Lambda or DynamoDB to see your running infrastructure.
Step 5 — Clean up
Section titled “Step 5 — Clean up”First, stop LocalStack to tear down all local resources:
bash lstk stop bash localstack stop LocalStack is ephemeral by default — stopping it removes all provisioned resources. To persist state across restarts, see Persistence or Cloud Pods.
Now that you’re done, you can remove the tutorial files from your machine (the Lambda source, zip, and, if you used Terraform, main.tf and state under the same folder):
rm -rf /tmp/localstack-demoNext steps
Section titled “Next steps”- Tutorials — Deeper dives into specific AWS services and application stacks
- Supported Services — Full list of emulated AWS services
- CI/CD Setup — Run LocalStack in GitHub Actions and other pipelines
- AI & Agent Workflows — Use LocalStack with AI coding tools and agents
- Tooling —
awslocal,tflocal, LocalStack Desktop, and more