Tools: Your Lambda Memory is Wrong: Auto-Tune It and Save 40% in Minutes ⚑

Tools: Your Lambda Memory is Wrong: Auto-Tune It and Save 40% in Minutes ⚑

Source: Dev.to

Most Lambda functions run with default 1024MB memory β€” wasting up to 40% of your bill. Here's how to deploy AWS Lambda Power Tuning with Terraform and find the perfect sweet spot. ## πŸ’Έ Why Memory Size Matters More Than You Think ## πŸ”§ What Is AWS Lambda Power Tuning? ## πŸ—οΈ Deploy Power Tuning with Terraform ## Step 1: Deploy the SAR Application ## Step 2: Create a Reusable Tuning Trigger ## Step 3: Schedule Monthly Tuning for All Functions ## Step 4: Apply Optimized Memory with Terraform ## ⚑ Quick Start: Tune One Function Right Now ## πŸ’° Real-World Savings Examples ## πŸ’‘ Pro Tips ## πŸ“Š TL;DR Pop quiz: Why did you set your Lambda memory to 1024MB? If the answer is "I don't know" or "it was the default" β€” you're probably overpaying by 30-40%. Here's the thing most people miss: Lambda memory also controls CPU. More memory = more CPU. But if your function doesn't need that extra power, you're paying for resources that sit idle every single invocation. Let me show you how to find the perfect memory size automatically. 🎯 Lambda pricing is directly proportional to memory: But here's the twist β€” more memory often means faster execution: The cheapest option isn't always the lowest memory. It's the best ratio of memory Γ— duration. Finding it manually? Nightmare. Finding it automatically? That's where Power Tuning comes in. AWS Lambda Power Tuning is an open-source Step Functions state machine that: It's built by AWS and battle-tested. Let's deploy it with Terraform. πŸš€ Once you get results, update your Lambda configs: Tag functions you want to auto-tune: Don't want the full setup? Run a one-off tune from the CLI: The output includes a visualization URL β€” paste it in your browser to see the cost/performance curve. πŸ“Š Notice the image processor actually increased memory β€” because the faster execution time made it cheaper overall. That's why guessing doesn't work. 🎯 Bottom line: Guessing Lambda memory is like guessing your shoe size β€” you'll walk, but it'll cost you. Let Power Tuning find the perfect fit. πŸ‘Ÿ Still running Lambda at 1024MB "because it works"? Run the tuner once and see how much you're wasting. The results will surprise you. 😏 Found this helpful? Follow for more AWS cost optimization with Terraform! πŸ’¬ Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: 128MB Γ— 1ms = $0.0000000021 512MB Γ— 1ms = $0.0000000083 1024MB Γ— 1ms = $0.0000000167 3008MB Γ— 1ms = $0.0000000489 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: 128MB Γ— 1ms = $0.0000000021 512MB Γ— 1ms = $0.0000000083 1024MB Γ— 1ms = $0.0000000167 3008MB Γ— 1ms = $0.0000000489 CODE_BLOCK: 128MB Γ— 1ms = $0.0000000021 512MB Γ— 1ms = $0.0000000083 1024MB Γ— 1ms = $0.0000000167 3008MB Γ— 1ms = $0.0000000489 CODE_BLOCK: Your API function at different memory sizes: 128MB: Duration 800ms β†’ Cost per invocation: $0.0000168 512MB: Duration 250ms β†’ Cost per invocation: $0.0000021 1024MB: Duration 200ms β†’ Cost per invocation: $0.0000033 ← Most people stop here 768MB: Duration 210ms β†’ Cost per invocation: $0.0000026 ← Actual sweet spot πŸ’° Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: Your API function at different memory sizes: 128MB: Duration 800ms β†’ Cost per invocation: $0.0000168 512MB: Duration 250ms β†’ Cost per invocation: $0.0000021 1024MB: Duration 200ms β†’ Cost per invocation: $0.0000033 ← Most people stop here 768MB: Duration 210ms β†’ Cost per invocation: $0.0000026 ← Actual sweet spot πŸ’° CODE_BLOCK: Your API function at different memory sizes: 128MB: Duration 800ms β†’ Cost per invocation: $0.0000168 512MB: Duration 250ms β†’ Cost per invocation: $0.0000021 1024MB: Duration 200ms β†’ Cost per invocation: $0.0000033 ← Most people stop here 768MB: Duration 210ms β†’ Cost per invocation: $0.0000026 ← Actual sweet spot πŸ’° COMMAND_BLOCK: # power-tuning/main.tf resource "aws_serverlessapplicationrepository_cloudformation_stack" "power_tuning" { name = "lambda-power-tuning" application_id = "arn:aws:serverlessrepo:us-east-1:451282441545:applications/aws-lambda-power-tuning" capabilities = [ "CAPABILITY_IAM", "CAPABILITY_NAMED_IAM" ] parameters = { # Memory values to test (comma-separated) PowerValues = "128,256,512,768,1024,1536,2048,3008" # Visualize results visualizationURL = "https://lambda-power-tuning.show/" # Timeout for each execution totalExecutionTimeout = "900" } tags = { Purpose = "cost-optimization" ManagedBy = "terraform" } } output "state_machine_arn" { value = aws_serverlessapplicationrepository_cloudformation_stack.power_tuning.outputs["StateMachineARN"] description = "ARN of the Power Tuning Step Function" } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: # power-tuning/main.tf resource "aws_serverlessapplicationrepository_cloudformation_stack" "power_tuning" { name = "lambda-power-tuning" application_id = "arn:aws:serverlessrepo:us-east-1:451282441545:applications/aws-lambda-power-tuning" capabilities = [ "CAPABILITY_IAM", "CAPABILITY_NAMED_IAM" ] parameters = { # Memory values to test (comma-separated) PowerValues = "128,256,512,768,1024,1536,2048,3008" # Visualize results visualizationURL = "https://lambda-power-tuning.show/" # Timeout for each execution totalExecutionTimeout = "900" } tags = { Purpose = "cost-optimization" ManagedBy = "terraform" } } output "state_machine_arn" { value = aws_serverlessapplicationrepository_cloudformation_stack.power_tuning.outputs["StateMachineARN"] description = "ARN of the Power Tuning Step Function" } COMMAND_BLOCK: # power-tuning/main.tf resource "aws_serverlessapplicationrepository_cloudformation_stack" "power_tuning" { name = "lambda-power-tuning" application_id = "arn:aws:serverlessrepo:us-east-1:451282441545:applications/aws-lambda-power-tuning" capabilities = [ "CAPABILITY_IAM", "CAPABILITY_NAMED_IAM" ] parameters = { # Memory values to test (comma-separated) PowerValues = "128,256,512,768,1024,1536,2048,3008" # Visualize results visualizationURL = "https://lambda-power-tuning.show/" # Timeout for each execution totalExecutionTimeout = "900" } tags = { Purpose = "cost-optimization" ManagedBy = "terraform" } } output "state_machine_arn" { value = aws_serverlessapplicationrepository_cloudformation_stack.power_tuning.outputs["StateMachineARN"] description = "ARN of the Power Tuning Step Function" } COMMAND_BLOCK: # power-tuning/trigger.tf # Lambda to kick off tuning runs and collect results resource "aws_lambda_function" "tuning_trigger" { filename = data.archive_file.trigger.output_path function_name = "power-tuning-trigger" role = aws_iam_role.trigger.arn handler = "index.handler" runtime = "python3.12" timeout = 900 source_code_hash = data.archive_file.trigger.output_base64sha256 environment { variables = { STATE_MACHINE_ARN = aws_serverlessapplicationrepository_cloudformation_stack.power_tuning.outputs["StateMachineARN"] SNS_TOPIC_ARN = aws_sns_topic.tuning_results.arn } } } data "archive_file" "trigger" { type = "zip" output_path = "${path.module}/trigger.zip" source { content = <<-PYTHON import boto3 import json import os import time sfn = boto3.client('stepfunctions') sns = boto3.client('sns') lambda_client = boto3.client('lambda') def handler(event, context): """ Trigger power tuning for a Lambda function. Event format: { "lambdaARN": "arn:aws:lambda:...:my-function", "num": 50, # invocations per memory level "payload": {}, # test payload "strategy": "cost" # or "speed" or "balanced" } """ lambda_arn = event['lambdaARN'] func_name = lambda_arn.split(':')[-1] # Get current memory for comparison config = lambda_client.get_function_configuration( FunctionName=func_name ) current_memory = config['MemorySize'] # Start power tuning input_payload = { "lambdaARN": lambda_arn, "powerValues": [128, 256, 512, 768, 1024, 1536, 2048, 3008], "num": event.get('num', 50), "payload": event.get('payload', {}), "strategy": event.get('strategy', 'cost'), "autoOptimize": False # Review before applying } execution = sfn.start_execution( stateMachineArn=os.environ['STATE_MACHINE_ARN'], input=json.dumps(input_payload) ) # Wait for completion exec_arn = execution['executionArn'] while True: status = sfn.describe_execution(executionArn=exec_arn) if status['status'] != 'RUNNING': break time.sleep(10) if status['status'] == 'SUCCEEDED': result = json.loads(status['output']) optimal_memory = result['power'] optimal_cost = result['cost'] # Calculate savings savings_pct = 0 if current_memory != optimal_memory: savings_pct = round( (1 - optimal_cost / (result['cost'] * current_memory / optimal_memory)) * 100 ) # Send results message = ( f"Lambda Power Tuning Results for {func_name}:\n\n" f"Current memory: {current_memory} MB\n" f"Optimal memory: {optimal_memory} MB\n" f"Strategy: {event.get('strategy', 'cost')}\n" f"Estimated savings: ~{savings_pct}%\n\n" f"Visualization: {result.get('stateMachine', {}).get('visualization', 'N/A')}\n\n" f"Action needed: Update {func_name} memory to {optimal_memory} MB" ) sns.publish( TopicArn=os.environ['SNS_TOPIC_ARN'], Subject=f'Power Tuning: {func_name} β†’ {optimal_memory}MB', Message=message ) return { 'function': func_name, 'current_memory': current_memory, 'optimal_memory': optimal_memory, 'savings_percent': savings_pct } else: raise Exception(f"Tuning failed: {status['status']}") PYTHON filename = "index.py" } } resource "aws_sns_topic" "tuning_results" { name = "lambda-tuning-results" } resource "aws_iam_role" "trigger" { name = "power-tuning-trigger-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }] }) } resource "aws_iam_role_policy" "trigger" { name = "power-tuning-trigger-policy" role = aws_iam_role.trigger.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "states:StartExecution", "states:DescribeExecution" ] Resource = "*" }, { Effect = "Allow" Action = ["sns:Publish"] Resource = aws_sns_topic.tuning_results.arn }, { Effect = "Allow" Action = [ "lambda:GetFunctionConfiguration" ] Resource = "*" }, { Effect = "Allow" Action = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] Resource = "arn:aws:logs:*:*:*" } ] }) } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: # power-tuning/trigger.tf # Lambda to kick off tuning runs and collect results resource "aws_lambda_function" "tuning_trigger" { filename = data.archive_file.trigger.output_path function_name = "power-tuning-trigger" role = aws_iam_role.trigger.arn handler = "index.handler" runtime = "python3.12" timeout = 900 source_code_hash = data.archive_file.trigger.output_base64sha256 environment { variables = { STATE_MACHINE_ARN = aws_serverlessapplicationrepository_cloudformation_stack.power_tuning.outputs["StateMachineARN"] SNS_TOPIC_ARN = aws_sns_topic.tuning_results.arn } } } data "archive_file" "trigger" { type = "zip" output_path = "${path.module}/trigger.zip" source { content = <<-PYTHON import boto3 import json import os import time sfn = boto3.client('stepfunctions') sns = boto3.client('sns') lambda_client = boto3.client('lambda') def handler(event, context): """ Trigger power tuning for a Lambda function. Event format: { "lambdaARN": "arn:aws:lambda:...:my-function", "num": 50, # invocations per memory level "payload": {}, # test payload "strategy": "cost" # or "speed" or "balanced" } """ lambda_arn = event['lambdaARN'] func_name = lambda_arn.split(':')[-1] # Get current memory for comparison config = lambda_client.get_function_configuration( FunctionName=func_name ) current_memory = config['MemorySize'] # Start power tuning input_payload = { "lambdaARN": lambda_arn, "powerValues": [128, 256, 512, 768, 1024, 1536, 2048, 3008], "num": event.get('num', 50), "payload": event.get('payload', {}), "strategy": event.get('strategy', 'cost'), "autoOptimize": False # Review before applying } execution = sfn.start_execution( stateMachineArn=os.environ['STATE_MACHINE_ARN'], input=json.dumps(input_payload) ) # Wait for completion exec_arn = execution['executionArn'] while True: status = sfn.describe_execution(executionArn=exec_arn) if status['status'] != 'RUNNING': break time.sleep(10) if status['status'] == 'SUCCEEDED': result = json.loads(status['output']) optimal_memory = result['power'] optimal_cost = result['cost'] # Calculate savings savings_pct = 0 if current_memory != optimal_memory: savings_pct = round( (1 - optimal_cost / (result['cost'] * current_memory / optimal_memory)) * 100 ) # Send results message = ( f"Lambda Power Tuning Results for {func_name}:\n\n" f"Current memory: {current_memory} MB\n" f"Optimal memory: {optimal_memory} MB\n" f"Strategy: {event.get('strategy', 'cost')}\n" f"Estimated savings: ~{savings_pct}%\n\n" f"Visualization: {result.get('stateMachine', {}).get('visualization', 'N/A')}\n\n" f"Action needed: Update {func_name} memory to {optimal_memory} MB" ) sns.publish( TopicArn=os.environ['SNS_TOPIC_ARN'], Subject=f'Power Tuning: {func_name} β†’ {optimal_memory}MB', Message=message ) return { 'function': func_name, 'current_memory': current_memory, 'optimal_memory': optimal_memory, 'savings_percent': savings_pct } else: raise Exception(f"Tuning failed: {status['status']}") PYTHON filename = "index.py" } } resource "aws_sns_topic" "tuning_results" { name = "lambda-tuning-results" } resource "aws_iam_role" "trigger" { name = "power-tuning-trigger-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }] }) } resource "aws_iam_role_policy" "trigger" { name = "power-tuning-trigger-policy" role = aws_iam_role.trigger.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "states:StartExecution", "states:DescribeExecution" ] Resource = "*" }, { Effect = "Allow" Action = ["sns:Publish"] Resource = aws_sns_topic.tuning_results.arn }, { Effect = "Allow" Action = [ "lambda:GetFunctionConfiguration" ] Resource = "*" }, { Effect = "Allow" Action = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] Resource = "arn:aws:logs:*:*:*" } ] }) } COMMAND_BLOCK: # power-tuning/trigger.tf # Lambda to kick off tuning runs and collect results resource "aws_lambda_function" "tuning_trigger" { filename = data.archive_file.trigger.output_path function_name = "power-tuning-trigger" role = aws_iam_role.trigger.arn handler = "index.handler" runtime = "python3.12" timeout = 900 source_code_hash = data.archive_file.trigger.output_base64sha256 environment { variables = { STATE_MACHINE_ARN = aws_serverlessapplicationrepository_cloudformation_stack.power_tuning.outputs["StateMachineARN"] SNS_TOPIC_ARN = aws_sns_topic.tuning_results.arn } } } data "archive_file" "trigger" { type = "zip" output_path = "${path.module}/trigger.zip" source { content = <<-PYTHON import boto3 import json import os import time sfn = boto3.client('stepfunctions') sns = boto3.client('sns') lambda_client = boto3.client('lambda') def handler(event, context): """ Trigger power tuning for a Lambda function. Event format: { "lambdaARN": "arn:aws:lambda:...:my-function", "num": 50, # invocations per memory level "payload": {}, # test payload "strategy": "cost" # or "speed" or "balanced" } """ lambda_arn = event['lambdaARN'] func_name = lambda_arn.split(':')[-1] # Get current memory for comparison config = lambda_client.get_function_configuration( FunctionName=func_name ) current_memory = config['MemorySize'] # Start power tuning input_payload = { "lambdaARN": lambda_arn, "powerValues": [128, 256, 512, 768, 1024, 1536, 2048, 3008], "num": event.get('num', 50), "payload": event.get('payload', {}), "strategy": event.get('strategy', 'cost'), "autoOptimize": False # Review before applying } execution = sfn.start_execution( stateMachineArn=os.environ['STATE_MACHINE_ARN'], input=json.dumps(input_payload) ) # Wait for completion exec_arn = execution['executionArn'] while True: status = sfn.describe_execution(executionArn=exec_arn) if status['status'] != 'RUNNING': break time.sleep(10) if status['status'] == 'SUCCEEDED': result = json.loads(status['output']) optimal_memory = result['power'] optimal_cost = result['cost'] # Calculate savings savings_pct = 0 if current_memory != optimal_memory: savings_pct = round( (1 - optimal_cost / (result['cost'] * current_memory / optimal_memory)) * 100 ) # Send results message = ( f"Lambda Power Tuning Results for {func_name}:\n\n" f"Current memory: {current_memory} MB\n" f"Optimal memory: {optimal_memory} MB\n" f"Strategy: {event.get('strategy', 'cost')}\n" f"Estimated savings: ~{savings_pct}%\n\n" f"Visualization: {result.get('stateMachine', {}).get('visualization', 'N/A')}\n\n" f"Action needed: Update {func_name} memory to {optimal_memory} MB" ) sns.publish( TopicArn=os.environ['SNS_TOPIC_ARN'], Subject=f'Power Tuning: {func_name} β†’ {optimal_memory}MB', Message=message ) return { 'function': func_name, 'current_memory': current_memory, 'optimal_memory': optimal_memory, 'savings_percent': savings_pct } else: raise Exception(f"Tuning failed: {status['status']}") PYTHON filename = "index.py" } } resource "aws_sns_topic" "tuning_results" { name = "lambda-tuning-results" } resource "aws_iam_role" "trigger" { name = "power-tuning-trigger-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }] }) } resource "aws_iam_role_policy" "trigger" { name = "power-tuning-trigger-policy" role = aws_iam_role.trigger.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "states:StartExecution", "states:DescribeExecution" ] Resource = "*" }, { Effect = "Allow" Action = ["sns:Publish"] Resource = aws_sns_topic.tuning_results.arn }, { Effect = "Allow" Action = [ "lambda:GetFunctionConfiguration" ] Resource = "*" }, { Effect = "Allow" Action = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] Resource = "arn:aws:logs:*:*:*" } ] }) } COMMAND_BLOCK: # power-tuning/scheduled.tf # Automatically tune your most expensive Lambdas every month resource "aws_lambda_function" "bulk_tuner" { filename = data.archive_file.bulk_tuner.output_path function_name = "power-tuning-bulk" role = aws_iam_role.trigger.arn handler = "index.handler" runtime = "python3.12" timeout = 900 source_code_hash = data.archive_file.bulk_tuner.output_base64sha256 environment { variables = { TRIGGER_FUNCTION = aws_lambda_function.tuning_trigger.function_name # Tag your Lambdas with PowerTune=true to include them TUNING_TAG = "PowerTune" } } } data "archive_file" "bulk_tuner" { type = "zip" output_path = "${path.module}/bulk_tuner.zip" source { content = <<-PYTHON import boto3 import json import os lambda_client = boto3.client('lambda') def handler(event, context): tag_key = os.environ['TUNING_TAG'] trigger_fn = os.environ['TRIGGER_FUNCTION'] # Find all Lambdas tagged for tuning paginator = lambda_client.get_paginator('list_functions') tuned = [] for page in paginator.paginate(): for fn in page['Functions']: tags = lambda_client.list_tags( Resource=fn['FunctionArn'] ).get('Tags', {}) if tags.get(tag_key) == 'true': # Trigger tuning lambda_client.invoke( FunctionName=trigger_fn, InvocationType='Event', # Async Payload=json.dumps({ 'lambdaARN': fn['FunctionArn'], 'num': 50, 'strategy': 'cost' }) ) tuned.append(fn['FunctionName']) return { 'functions_tuned': len(tuned), 'functions': tuned } PYTHON filename = "index.py" } } # Run monthly resource "aws_cloudwatch_event_rule" "monthly_tuning" { name = "monthly-lambda-tuning" schedule_expression = "rate(30 days)" } resource "aws_cloudwatch_event_target" "bulk_tuner" { rule = aws_cloudwatch_event_rule.monthly_tuning.name arn = aws_lambda_function.bulk_tuner.arn } resource "aws_lambda_permission" "allow_eventbridge" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.bulk_tuner.function_name principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.monthly_tuning.arn } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: # power-tuning/scheduled.tf # Automatically tune your most expensive Lambdas every month resource "aws_lambda_function" "bulk_tuner" { filename = data.archive_file.bulk_tuner.output_path function_name = "power-tuning-bulk" role = aws_iam_role.trigger.arn handler = "index.handler" runtime = "python3.12" timeout = 900 source_code_hash = data.archive_file.bulk_tuner.output_base64sha256 environment { variables = { TRIGGER_FUNCTION = aws_lambda_function.tuning_trigger.function_name # Tag your Lambdas with PowerTune=true to include them TUNING_TAG = "PowerTune" } } } data "archive_file" "bulk_tuner" { type = "zip" output_path = "${path.module}/bulk_tuner.zip" source { content = <<-PYTHON import boto3 import json import os lambda_client = boto3.client('lambda') def handler(event, context): tag_key = os.environ['TUNING_TAG'] trigger_fn = os.environ['TRIGGER_FUNCTION'] # Find all Lambdas tagged for tuning paginator = lambda_client.get_paginator('list_functions') tuned = [] for page in paginator.paginate(): for fn in page['Functions']: tags = lambda_client.list_tags( Resource=fn['FunctionArn'] ).get('Tags', {}) if tags.get(tag_key) == 'true': # Trigger tuning lambda_client.invoke( FunctionName=trigger_fn, InvocationType='Event', # Async Payload=json.dumps({ 'lambdaARN': fn['FunctionArn'], 'num': 50, 'strategy': 'cost' }) ) tuned.append(fn['FunctionName']) return { 'functions_tuned': len(tuned), 'functions': tuned } PYTHON filename = "index.py" } } # Run monthly resource "aws_cloudwatch_event_rule" "monthly_tuning" { name = "monthly-lambda-tuning" schedule_expression = "rate(30 days)" } resource "aws_cloudwatch_event_target" "bulk_tuner" { rule = aws_cloudwatch_event_rule.monthly_tuning.name arn = aws_lambda_function.bulk_tuner.arn } resource "aws_lambda_permission" "allow_eventbridge" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.bulk_tuner.function_name principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.monthly_tuning.arn } COMMAND_BLOCK: # power-tuning/scheduled.tf # Automatically tune your most expensive Lambdas every month resource "aws_lambda_function" "bulk_tuner" { filename = data.archive_file.bulk_tuner.output_path function_name = "power-tuning-bulk" role = aws_iam_role.trigger.arn handler = "index.handler" runtime = "python3.12" timeout = 900 source_code_hash = data.archive_file.bulk_tuner.output_base64sha256 environment { variables = { TRIGGER_FUNCTION = aws_lambda_function.tuning_trigger.function_name # Tag your Lambdas with PowerTune=true to include them TUNING_TAG = "PowerTune" } } } data "archive_file" "bulk_tuner" { type = "zip" output_path = "${path.module}/bulk_tuner.zip" source { content = <<-PYTHON import boto3 import json import os lambda_client = boto3.client('lambda') def handler(event, context): tag_key = os.environ['TUNING_TAG'] trigger_fn = os.environ['TRIGGER_FUNCTION'] # Find all Lambdas tagged for tuning paginator = lambda_client.get_paginator('list_functions') tuned = [] for page in paginator.paginate(): for fn in page['Functions']: tags = lambda_client.list_tags( Resource=fn['FunctionArn'] ).get('Tags', {}) if tags.get(tag_key) == 'true': # Trigger tuning lambda_client.invoke( FunctionName=trigger_fn, InvocationType='Event', # Async Payload=json.dumps({ 'lambdaARN': fn['FunctionArn'], 'num': 50, 'strategy': 'cost' }) ) tuned.append(fn['FunctionName']) return { 'functions_tuned': len(tuned), 'functions': tuned } PYTHON filename = "index.py" } } # Run monthly resource "aws_cloudwatch_event_rule" "monthly_tuning" { name = "monthly-lambda-tuning" schedule_expression = "rate(30 days)" } resource "aws_cloudwatch_event_target" "bulk_tuner" { rule = aws_cloudwatch_event_rule.monthly_tuning.name arn = aws_lambda_function.bulk_tuner.arn } resource "aws_lambda_permission" "allow_eventbridge" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.bulk_tuner.function_name principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.monthly_tuning.arn } COMMAND_BLOCK: # Before tuning (guessing) resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 1024 # ← "seemed right" 🀷 # ... } # After tuning (optimized) βœ… resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 512 # ← Power Tuning says this is optimal πŸ’° # ... } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: # Before tuning (guessing) resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 1024 # ← "seemed right" 🀷 # ... } # After tuning (optimized) βœ… resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 512 # ← Power Tuning says this is optimal πŸ’° # ... } COMMAND_BLOCK: # Before tuning (guessing) resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 1024 # ← "seemed right" 🀷 # ... } # After tuning (optimized) βœ… resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 512 # ← Power Tuning says this is optimal πŸ’° # ... } COMMAND_BLOCK: resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 512 # ... tags = { PowerTune = "true" # πŸ‘ˆ Include in monthly tuning } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 512 # ... tags = { PowerTune = "true" # πŸ‘ˆ Include in monthly tuning } } COMMAND_BLOCK: resource "aws_lambda_function" "my_api" { function_name = "my-api-handler" memory_size = 512 # ... tags = { PowerTune = "true" # πŸ‘ˆ Include in monthly tuning } } COMMAND_BLOCK: # Start tuning aws stepfunctions start-execution \ --state-machine-arn "arn:aws:states:us-east-1:123456:stateMachine:powerTuningStateMachine" \ --input '{ "lambdaARN": "arn:aws:lambda:us-east-1:123456:function:my-function", "powerValues": [128, 256, 512, 768, 1024, 1536, 2048, 3008], "num": 50, "strategy": "cost", "payload": {} }' # Check results aws stepfunctions describe-execution \ --execution-arn "arn:aws:states:us-east-1:123456:execution:powerTuningStateMachine:xxx" Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: # Start tuning aws stepfunctions start-execution \ --state-machine-arn "arn:aws:states:us-east-1:123456:stateMachine:powerTuningStateMachine" \ --input '{ "lambdaARN": "arn:aws:lambda:us-east-1:123456:function:my-function", "powerValues": [128, 256, 512, 768, 1024, 1536, 2048, 3008], "num": 50, "strategy": "cost", "payload": {} }' # Check results aws stepfunctions describe-execution \ --execution-arn "arn:aws:states:us-east-1:123456:execution:powerTuningStateMachine:xxx" COMMAND_BLOCK: # Start tuning aws stepfunctions start-execution \ --state-machine-arn "arn:aws:states:us-east-1:123456:stateMachine:powerTuningStateMachine" \ --input '{ "lambdaARN": "arn:aws:lambda:us-east-1:123456:function:my-function", "powerValues": [128, 256, 512, 768, 1024, 1536, 2048, 3008], "num": 50, "strategy": "cost", "payload": {} }' # Check results aws stepfunctions describe-execution \ --execution-arn "arn:aws:states:us-east-1:123456:execution:powerTuningStateMachine:xxx" - Runs your Lambda at multiple memory sizes (e.g., 128, 256, 512, 768, 1024, 1536, 2048, 3008) - Executes it multiple times at each level for accuracy - Produces a cost vs performance graph - Tells you the cheapest and fastest memory configuration - Always use a realistic payload β€” Don't tune with empty {} if your function processes data. Results will be meaningless - CPU-bound vs I/O-bound matters β€” CPU-bound functions benefit from more memory (more CPU). I/O-bound functions (waiting on APIs/databases) often run cheapest at low memory - Tune after code changes β€” A refactor can shift the optimal memory. Monthly tuning catches this - Three strategies available: cost β€” Find the cheapest memory (most common) πŸ’° speed β€” Find the fastest memory ⚑ balanced β€” Best cost-to-performance ratio βš–οΈ - cost β€” Find the cheapest memory (most common) πŸ’° - speed β€” Find the fastest memory ⚑ - balanced β€” Best cost-to-performance ratio βš–οΈ - Combine with Graviton β€” ARM Lambdas are 20% cheaper. Tune after migrating to ARM for compounding savings πŸ”₯ - cost β€” Find the cheapest memory (most common) πŸ’° - speed β€” Find the fastest memory ⚑ - balanced β€” Best cost-to-performance ratio βš–οΈ