AWS Lambda skill for Codex

Faster cold starts, no reprocessed batches. Codex tunes Lambda functions from REPORT log numbers rather than guesses: init duration, billed duration and max memory drive packaging, memory and architecture choices. It keeps handlers thin with clients created at init, gives every trigger a defined retry and dead-letter path, and uses reserved or event-source concurrency to keep downstream databases alive.

  • Cloud Platforms
  • $aws-lambda
  • 202 lines
  • 2 files
  • ~2,300 tokens
  • MIT
  • Updated
  • Editor's pick

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- aws-lambda

Installs to ~/.agents/skills/aws-lambda/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $aws-lambda, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Builds, packages and tunes AWS Lambda functions in Node.js, Python, Java and Go - handler structure, zip vs container image packaging, layers, cold start reduction with SnapStart and provisioned concurrency, memory right-sizing, reserved concurrency, SQS partial batch failures and timeouts. Use when writing a Lambda handler, the function is slow to start or times out, the deployment package is too large, throttling or concurrency errors appear, or a queue-triggered function reprocesses messages.

Use it when

  • Writing or refactoring a Lambda handler in Node.js, Python, Java or Go
  • Cold starts, timeouts or out-of-memory errors in a function
  • A Lambda overwhelms a database or gets throttled
  • A queue or stream trigger retries the wrong records

Use something else when

  • You need the SAM template, local emulation and deploy pipeline: use aws-sam
  • The work is the HTTP layer in front of the function: use aws-api-gateway

How to check the result

Invoke with a representative event and compare Init Duration, p99 Duration and Max Memory Used from REPORT lines before and after the change; a poison SQS message lands alone in the DLQ.

The mistake it prevents

Throttling an SQS consumer with reserved concurrency instead of the event source mapping's maximum concurrency, which bounces messages into the DLQ.

~/.agents/skills/aws-lambda/SKILL.md

--- name: aws-lambda description: Builds, packages and tunes AWS Lambda functions in Node.js, Python, Java and Go - handler structure, zip vs container image packaging, layers, cold… license: MIT ---

Done well means: the handler does per-request work only, init is small and measured, memory is chosen from data rather than defaults, every trigger has a defined failure path (retry, DLQ or destination), and concurrency limits protect the downstream systems.

Before changing anything

  1. Read AGENTS.md and find how the function is deployed (CDK, SAM, Terraform, Serverless Framework, plain zip). Change the IaC, not the console.
  2. Note runtime, architecture, memory, timeout, trigger type and whether it is in a VPC.
  3. Pull real numbers if credentials and network are available:
aws lambda get-function-configuration --function-name orders-api \
  --query '{rt:Runtime,arch:Architectures,mem:MemorySize,timeout:Timeout,layers:Layers[].Arn}'
aws logs filter-log-events --log-group-name /aws/lambda/orders-api \
  --filter-pattern '"REPORT"' --max-items 50 --query 'events[].message'

REPORT lines carry Duration, Billed Duration, Max Memory Used and, on cold starts, Init Duration. These are the inputs for every tuning decision below.

Handler structure

Create clients, read config and load models outside the handler so warm invocations reuse them. Keep the handler thin and testable.

import json, os
import boto3
from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.typing import LambdaContext

logger = Logger()
table = boto3.resource("dynamodb").Table(os.environ["TABLE_NAME"])  # init once

@logger.inject_lambda_context
def handler(event: dict, context: LambdaContext) -> dict:
    order = json.loads(event["body"])
    table.put_item(Item=order, ConditionExpression="attribute_not_exists(pk)")
    return {"statusCode": 201, "body": json.dumps({"id": order["pk"]})}
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
const ddb = new DynamoDBClient({});           // init once, reused while warm

export const handler = async (event: APIGatewayProxyEventV2) => {
  // per-request work only
};
  • Node.js: use ESM or CommonJS consistently, and the AWS SDK v3 modular clients. The SDK v3 is in the managed runtime, but bundle your own pinned copy for reproducible behaviour.
  • Never store per-request data in module scope; warm containers serve many requests.
  • Read context.get_remaining_time_in_millis() (Python) or context.getRemainingTimeInMillis() (Node) before starting long work.

Packaging

SituationPackage as
Code plus dependencies under 250 MB unzippedZip, bundled (esbuild for Node, pip install -t for Python)
Native deps, ML models, >250 MB, or you want one build artifact for many toolsContainer image (up to 10 GB)
Shared dependency used by many functions, changes rarelyLayer, versioned, max 5 per function
Shared code that changes oftenNot a layer: bundle it into each function

Limits to design around: 50 MB zipped for direct upload (use S3 above that), 250 MB unzipped including layers, 6 MB synchronous request/response payload, 1 MB asynchronous event payload, 15-minute timeout, 128 MB to 10,240 MB memory, /tmp 512 MB default and up to 10,240 MB.

Build Python dependencies for the Lambda platform, not your laptop:

pip install -r requirements.txt -t build/ \
  --platform manylinux2014_aarch64 --only-binary=:all: --python-version 3.13

Match --platform to the function architecture (manylinux2014_x86_64 for x86_64). Installing packages needs network; ask before running it in a sandbox.

Cold starts

Measure first: compare Init Duration against the latency budget. Then work down this list, cheapest first.

  1. Shrink init. Bundle and tree-shake (esbuild --minify, exclude unused SDK clients), lazy-import heavy modules used by few code paths, avoid reading large files at import.
  2. Raise memory. CPU scales with memory; 1,769 MB gives one full vCPU. Init and duration often drop enough that cost stays flat.
  3. arm64. Usually cheaper per GB-second and as fast or faster; test native deps.
  4. SnapStart for Java, Python and .NET managed runtimes: snapshots the initialized environment. Requires published versions or aliases; re-seed randomness and re-establish network connections in the runtime hooks, because every restored environment starts from the same memory image.
  5. Provisioned concurrency on an alias for strict p99 latency. Costs money even when idle; schedule it with Application Auto Scaling for business hours.

Note that the init phase is billed for on-demand functions, so a heavy init costs on every cold start, not just latency.

VPC attachment no longer adds the multi-second cold start it once did, but the function needs NAT or VPC endpoints to reach AWS APIs and the internet.

Memory right-sizing

Test at several memory sizes with the same payload and pick the knee of the cost curve. AWS Lambda Power Tuning (a Step Functions app) automates this. Rules of thumb:

  • Max Memory Used near the limit: raise memory, you are risking OOM.
  • CPU-bound work: duration falls almost linearly with memory up to about 1.8 GB, so cost barely changes and latency improves.
  • I/O-bound work waiting on a database: extra memory buys little; stay small.

Concurrency

  • Account default is 1,000 concurrent executions per region (raisable). Each function can scale by 1,000 concurrent executions every 10 seconds.
  • Reserved concurrency caps a function and guarantees it capacity. Use it to protect a database: 50 reserved concurrency means at most 50 connections from this function. Setting it to 0 stops all invocations (a kill switch).
  • Provisioned concurrency pre-initializes environments; it does not cap anything.
  • Throttled synchronous calls return 429 to the caller; throttled async events are retried for up to 6 hours, then go to the failure destination or DLQ.

For relational databases, use RDS Proxy or a small pool size of 1 per environment.

Triggers and failure handling

TriggerRetriesConfigure
API Gateway / Function URL (sync)None by Lambda; caller decidesTimeout below the API’s 29-30 s limit
Async (S3, SNS, EventBridge)2 retries by defaultMaximumRetryAttempts, MaximumEventAgeInSeconds, on-failure destination
SQSUntil message expires or hits maxReceiveCountRedrive policy to a DLQ, partial batch responses
Kinesis / DynamoDB StreamsUntil record expires; blocks the shardBisectBatchOnFunctionError, MaximumRetryAttempts, on-failure destination

SQS partial batch failure: enable ReportBatchItemFailures in the event source mapping’s FunctionResponseTypes and return only the failed IDs. Without it one bad message makes the whole batch visible again and the good ones are reprocessed.

def handler(event, context):
    failures = []
    for record in event["Records"]:
        try:
            process(json.loads(record["body"]))
        except Exception:
            logger.exception("failed", extra={"id": record["messageId"]})
            failures.append({"itemIdentifier": record["messageId"]})
    return {"batchItemFailures": failures}

Set the queue visibility timeout to at least 6 times the function timeout. Use the event source mapping’s maximum concurrency setting (minimum 2) rather than reserved concurrency to throttle an SQS consumer; reserved concurrency throttling makes messages bounce and burn receive counts.

All async and queue triggers are at-least-once. Make handlers idempotent (conditional writes, Powertools idempotency utility with a DynamoDB table).

Local testing

  • Unit-test the business logic without Lambda at all; pass a plain dict event.
  • sam local invoke Fn -e events/sqs.json emulates the runtime and needs Docker.
  • Generate realistic events with sam local generate-event sqs receive-message.

Gotchas

  • Timeout of 3 s (the default) on a function calling a slow API; set it from p99 downstream latency plus headroom, and set SDK client timeouts lower than the function’s.
  • A Lambda that writes to the S3 bucket that triggers it loops. Lambda detects some recursive loops and stops them, but use a separate prefix or bucket.
  • Log groups default to never expire. Set retention.
  • Environment variables are visible to anyone with lambda:GetFunctionConfiguration. Keep secrets in Secrets Manager or Parameter Store and cache them (Parameters and Secrets Lambda extension or Powertools parameters).
  • $LATEST cannot have provisioned concurrency or SnapStart; publish versions and point an alias at them.
  • Deleting a function or changing a production alias needs the user’s confirmation.

Verification

  • Invoke with a representative event: aws lambda invoke --function-name f --cli-binary-format raw-in-base64-out --payload file://event.json out.json and inspect out.json and the FunctionError field.
  • Compare Init Duration, p50/p99 Duration and Max Memory Used before and after.
  • For SQS consumers, inject one poison message and confirm only it reaches the DLQ.

Output

Report: configuration changed (memory, timeout, arch, concurrency) with the before/after numbers that justified it, failure path per trigger, and any cost impact (provisioned concurrency, larger memory).

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "AWS Lambda"
  short_description: "Faster cold starts, no reprocessed batches"
  default_prompt: "$aws-lambda Our Lambda cold start is 4 seconds, figure out why and fix it"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem