Production Lambda deployment uses Infrastructure as Code, automated pipelines, and proper environment separation.

AWS SAM — Infrastructure as Code

  # template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Production Lambda API

Globals:
  Function:
    Timeout: 30
    MemorySize: 512
    Runtime: python3.12
    Environment:
      Variables:
        ENV: production
        LOG_LEVEL: INFO

Resources:
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.handler
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref DataTable
      Events:
        Api:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY
      Tags:
        Project: my-api
        Environment: production

  DataTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: id
        Type: String

Outputs:
  ApiUrl:
    Description: API Gateway endpoint
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
  

Deploy:

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

Environment Separation

  # Development
sam deploy --config-env dev --parameter-overrides Env=dev

# Staging
sam deploy --config-env staging

# Production
sam deploy --config-env prod
  
  # samconfig.toml
[dev.deploy.parameters]
stack_name = "my-api-dev"
parameter_overrides = "Env=dev"

[prod.deploy.parameters]
stack_name = "my-api-prod"
parameter_overrides = "Env=prod"
confirm_changeset = true
  

Secrets with AWS Secrets Manager

  import boto3
import json
import os

def get_secret(secret_name):
    client = boto3.client("secretsmanager")
    response = client.get_secret_value(SecretId=secret_name)
    return json.loads(response["SecretString"])

# Cache at module level
_secrets = None

def get_config():
    global _secrets
    if _secrets is None:
        _secrets = get_secret(os.environ["SECRET_ARN"])
    return _secrets
  

Grant permission in SAM:

  Policies:
  - AWSSecretsManagerGetSecretValuePolicy:
      SecretArn: !Ref AppSecret
  

CI/CD with GitHub Actions

  # .github/workflows/deploy.yml
name: Deploy Lambda

on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  test-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - run: pip install -r requirements.txt pytest
      - run: pytest tests/ -v

      - uses: aws-actions/setup-sam@v2
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: us-east-1

      - run: sam build
      - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset
  

Use OIDC for credentials — no long-lived access keys in GitHub secrets.

CloudWatch Monitoring

Structured Logging

  import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
    logger.info(json.dumps({
        "request_id": context.aws_request_id,
        "path": event.get("path"),
        "method": event.get("httpMethod"),
    }))
  

Alarms

  # In SAM template
HighErrorRateAlarm:
  Type: AWS::CloudWatch::Alarm
  Properties:
    AlarmName: !Sub "${AWS::StackName}-high-error-rate"
    MetricName: Errors
    Namespace: AWS/Lambda
    Statistic: Sum
    Period: 300
    EvaluationPeriods: 1
    Threshold: 10
    ComparisonOperator: GreaterThanThreshold
    Dimensions:
      - Name: FunctionName
        Value: !Ref ApiFunction
  

Custom Domain

  # SAM with custom domain
ApiFunction:
  Type: AWS::Serverless::Function
  Properties:
    Events:
      Api:
        Type: Api
        Properties:
          RestApiId: !Ref CustomApi

CustomApi:
  Type: AWS::Serverless::Api
  Properties:
    StageName: prod
    Domain:
      DomainName: api.example.com
      CertificateArn: !Ref Certificate
      Route53:
        HostedZoneId: Z1234567890
  

Rollback with Aliases and Versions

  # Publish a version
aws lambda publish-version --function-name my-api

# Create alias pointing to version
aws lambda create-alias \
    --function-name my-api \
    --name production \
    --function-version 5

# Rollback — point alias to previous version
aws lambda update-alias \
    --function-name my-api \
    --name production \
    --function-version 4
  

Cost Optimization

Strategy Savings
ARM64 (Graviton) ~20% cheaper
Right-size memory Faster execution = lower cost
Reserved concurrency cap Prevent runaway bills
SQS batching Fewer invocations
Provisioned concurrency Only when latency-critical
  # Graviton in SAM
Metadata:
  BuildMethod: python3.12
  BuildArchitecture: arm64
  

Production Checklist

  • IAM least-privilege role per function
  • Secrets in Secrets Manager, not env vars in code
  • CloudWatch alarms for errors and duration
  • X-Ray tracing enabled for debugging
  • Dead Letter Queue for failed async invocations
  • VPC only if accessing private resources
  • confirm_changeset = true for production deploys
  • Automated tests in CI before deploy

All three cloud platforms now have complete 4-chapter serverless tracks: Start → Advanced → Best Practices → Deployment.