On this page
article
Deploying AWS Lambda
Deploy Python Lambda functions to production — SAM, CI/CD with GitHub Actions, environments, monitoring, and rollback strategies.
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 = truefor production deploys - Automated tests in CI before deploy
Related Chapters
All three cloud platforms now have complete 4-chapter serverless tracks: Start → Advanced → Best Practices → Deployment.