Systematic upgrade of deprecated AWS Lambda runtimes across a production environment, migrating 100+ functions from end-of-life Node.js runtimes to nodejs20.x.
This project documents the runtime remediation effort across a Lambda-heavy AWS environment. The environment contained functions across multiple deprecated Node.js runtime versions, with the majority on nodejs16.x and a long tail of older EOL versions.
Left unaddressed, deprecated runtimes result in Lambda functions that can no longer be updated, may be blocked from invocation in future AWS enforcement actions, and represent an unpatched security surface.
| Runtime | Status | Notes |
|---|---|---|
nodejs20.x |
✅ Current | Target runtime |
nodejs16.x |
111 functions | |
nodejs14.x |
❌ EOL | Present |
nodejs12.x |
❌ EOL | Present |
nodejs8.10 |
❌ EOL (2019) | Present |
nodejs6.10 |
❌ EOL (2019) | Present |
# List all Lambda functions and their runtimes
aws lambda list-functions \
--query 'Functions[*].{Name:FunctionName,Runtime:Runtime}' \
--output table
# Filter for deprecated runtimes only
aws lambda list-functions \
--query 'Functions[?Runtime==`nodejs16.x`].{Name:FunctionName,Runtime:Runtime}' \
--output table- Check for deprecated Node.js APIs in function code
- Review package.json for outdated dependencies
- Test in a dev/staging Lambda alias first
# Update a single function's runtime
aws lambda update-function-configuration \
--function-name <function-name> \
--runtime nodejs20.x
# Verify the update
aws lambda get-function-configuration \
--function-name <function-name> \
--query '{Name:FunctionName,Runtime:Runtime}'# Invoke function with test payload
aws lambda invoke \
--function-name <function-name> \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
response.json && cat response.json#!/bin/bash
FUNCTIONS=$(aws lambda list-functions \
--query 'Functions[?Runtime==`nodejs16.x`].FunctionName' \
--output text)
for fn in $FUNCTIONS; do
echo "Upgrading: $fn"
aws lambda update-function-configuration \
--function-name "$fn" --runtime nodejs20.x
echo "Done: $fn"
donelambda-runtime-remediation/
├── README.md
├── findings/
│ └── runtime-inventory.md
├── change-records/
│ ├── CR-nodejs16-batch-upgrade.md
│ └── CR-legacy-runtime-cleanup.md
└── scripts/
├── audit-lambda-runtimes.sh
├── bulk-upgrade-nodejs16.sh
└── validate-lambda-invoke.sh
- AWS Lambda, AWS CLI
- Node.js (nodejs20.x target)
- Bash
All function names and ARNs have been sanitized.
Beyond runtime remediation, Lambda was used to build a scheduled cost-saving automation for a non-production database cluster that did not need to run outside business hours.
The pattern: an EventBridge Scheduler triggers a Lambda function on a cron schedule to stop the cluster in the evening and start it again the next morning, on weekdays only. This cut the cluster's compute cost to roughly a third of its 24/7 baseline.
| Component | Configuration |
|---|---|
| Trigger | EventBridge Scheduler (cron) |
| Runtime | Python 3.14 |
| Stop schedule | 18:00, Mon–Fri |
| Start schedule | 07:00, Mon–Fri |
| Timezone | Anchored to local business timezone |
import boto3
rds = boto3.client('rds')
CLUSTER_ID = '<dev-cluster-id>'
def lambda_handler(event, context):
action = event.get('action') # 'stop' or 'start'
if action == 'stop':
rds.stop_db_cluster(DBClusterIdentifier=CLUSTER_ID)
elif action == 'start':
rds.start_db_cluster(DBClusterIdentifier=CLUSTER_ID)
return {'cluster': CLUSTER_ID, 'action': action}aws scheduler create-schedule \
--name dev-db-stop \
--schedule-expression "cron(0 18 ? * MON-FRI *)" \
--schedule-expression-timezone "America/Chicago" \
--flexible-time-window '{"Mode":"OFF"}' \
--target '{"Arn":"<lambda-arn>","RoleArn":"<scheduler-role-arn>","Input":"{\"action\":\"stop\"}"}'A key design note: scheduled stop/start only suits non-production workloads where a short morning warm-up is acceptable. Production clusters were explicitly excluded from this automation.
