Engineering

AWS Lambda with Python: Local to Production in One Hour

May 2026 · 12 min read

AWS Lambda + Python is the path of least resistance for serverless backends inside AWS. The free tier (1M requests/month, 400,000 GB-seconds compute) covers most small workloads at zero cost. Here's a complete walkthrough from local development to production deploy in roughly an hour.

Step 1: handler signature

A Lambda function is a Python function with this signature:

def handler(event: dict, context) -> dict:
    return {"statusCode": 200, "body": "ok"}

The event shape depends on what triggers the Lambda. API Gateway sends an HTTP-style event; SQS sends a batch of messages; S3 sends an object event. context gives runtime info (remaining time, log group, request ID).

Step 2: project layout

my-lambda/
├── src/
│   ├── handler.py
│   └── requirements.txt
├── template.yaml        # SAM CloudFormation template
├── events/
│   └── api_event.json   # sample event for local testing
└── samconfig.toml

Step 3: handler.py with Powertools

from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.utilities.typing import LambdaContext

logger = Logger()
tracer = Tracer()
app = APIGatewayRestResolver()

@app.get("/hello/<name>")
@tracer.capture_method
def hello(name: str):
    logger.info("greeting requested", extra={"name": name})
    return {"message": f"Hello, {name}!"}

@logger.inject_lambda_context(correlation_id_path="requestContext.requestId")
@tracer.capture_lambda_handler
def handler(event: dict, context: LambdaContext) -> dict:
    return app.resolve(event, context)

Powertools gives you structured JSON logging, distributed tracing (X-Ray), and a clean route decorator. All 3 in one library.

Step 4: SAM template.yaml

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: python3.12
    MemorySize: 256
    Timeout: 10
    Tracing: Active
    Environment:
      Variables:
        POWERTOOLS_SERVICE_NAME: my-api
        LOG_LEVEL: INFO

Resources:
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: handler.handler
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref EventsTable

  EventsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST

Step 5: local testing with SAM CLI

brew install aws-sam-cli
sam build
sam local invoke ApiFunction -e events/api_event.json
sam local start-api  # spins up HTTP server on localhost:3000

Sample event in events/api_event.json:

{
  "httpMethod": "GET",
  "path": "/hello/world",
  "headers": {},
  "queryStringParameters": null,
  "body": null,
  "requestContext": {"requestId": "test-123"}
}

Step 6: deploy

sam deploy --guided  # first time
sam deploy           # subsequent

Outputs the API Gateway URL. Hit it: curl https://abc.execute-api.eu-west-1.amazonaws.com/Prod/hello/world

Step 7: IAM least privilege

SAM has policy templates (DynamoDBReadPolicy, S3ReadPolicy, etc.) that grant only what you need. Avoid AdministratorAccess and avoid inline policies that grant * resources.

Pattern:

Policies:
  - DynamoDBCrudPolicy:
      TableName: !Ref EventsTable
  - S3WritePolicy:
      BucketName: my-bucket
  - Statement:
      - Effect: Allow
        Action: secretsmanager:GetSecretValue
        Resource: arn:aws:secretsmanager:*:*:secret:my-app/*

Step 8: secrets

Don't put secrets in template.yaml or environment variables of the Lambda definition. Use AWS Secrets Manager or SSM Parameter Store. Fetch at cold start:

from aws_lambda_powertools.utilities import parameters

@functools.lru_cache(maxsize=1)
def get_db_password():
    return parameters.get_secret("my-app/db-password")

Step 9: observability

Logs go to CloudWatch automatically. With Powertools' @logger.inject_lambda_context, every log line has the request ID, function name, and any structured fields you add. Search in CloudWatch Logs Insights:

fields @timestamp, message, name
| filter level = "ERROR"
| sort @timestamp desc
| limit 50

For alerts: CloudWatch Alarms on error count, duration p99, throttles.

Step 10: cost

TierFreePaid
Requests1M / month0.20 USD per 1M
Compute (GB-seconds)400,000 / month0.0000166667 USD per GB-second
API Gateway requests1M / month (first 12 months)3.50 USD per 1M after

A 256 MB Lambda running 100ms per request, called 100,000 times per month: 100k requests + 2,560 GB-seconds. Well within free tier.

Comparison

Item AWS Lambda Cloudflare Workers Vercel Functions
Free tier requests/month 1M 100k/day = 3M/month 100k/day
Cold start (Python) 200-800ms 0ms 50-300ms
Max execution time 15 min 30 sec paid 10 sec hobby
Memory max 10 GB 128 MB 1 GB
Language flexibility Any JS/TS only JS/TS/Python/Go
Pricing model Per request + GB-sec Per request Per execution + bandwidth
Best for Long jobs, AWS native Edge HTTP APIs Next.js / frontend tied

FAQ

Why Powertools over plain Lambda?
Structured logging, X-Ray tracing, idempotency, validation, parameter caching — all in one library. Plain Lambda lacks all of these out of the box.

How do I reduce cold starts?
Smaller package size, Python 3.12+ (faster startup than 3.10), provisioned concurrency for hot paths (costs extra), SnapStart for Java/Python (10x faster cold start, paid feature).

Should I use Lambda Layers?
For heavy dependencies (Pandas, PIL, ML libs) that don't change often, yes — keeps function package small. For app deps, just include in the function package.

Container images vs zip?
Container images for >50 MB packages or when you need specific OS libraries. Zip for everything else — faster cold starts and simpler deploys.

Need a Lambda backend built right?

We've shipped Python Lambda services for 8 European startups. SAM, Powertools, X-Ray, IaC included.

Book a discovery call

Related Posts

Cloudflare Workers Tutorial Docker Dev Workflow
← All blog posts

From localhost to production in 1 hour

SAM CLI, Powertools, X-Ray. We build Python Lambdas for European SaaS.

Book a discovery call