After managing Lambda functions processing millions of invocations monthly, we’ve learned a few things about keeping costs under control.
Lambda pricing seems simple: pay per invocation and duration. But the real costs often come from:
The default 128MB is almost never right. But neither is blindly increasing to 1GB.
# Before: 128MB, 2000ms average
def handler(event, context):
data = process_large_json(event['body']) # CPU-bound
return {'statusCode': 200, 'body': json.dumps(data)}
# After: 512MB, 400ms average
# Cost: Actually LOWER due to reduced duration
Use AWS Lambda Power Tuning to find your sweet spot. Often, 512MB or 1024MB is the cost-optimal choice for CPU-bound workloads.
Every Lambda invocation creating a new database connection is a recipe for:
// Use RDS Proxy or connection pooling
import { Pool } from 'pg';
// Connection reused across warm invocations
const pool = new Pool({
host: process.env.RDS_PROXY_ENDPOINT,
max: 1, // Single connection per Lambda instance
});
export const handler = async (event) => {
const client = await pool.connect();
try {
const result = await client.query('SELECT ...');
return { statusCode: 200, body: JSON.stringify(result.rows) };
} finally {
client.release();
}
};
Provisioned concurrency eliminates cold starts but adds cost. Use it when:
# Calculate break-even point
# Provisioned: $0.000004463 per GB-second
# On-demand: $0.0000166667 per GB-second
# If your cold start rate > ~27%, provisioned wins
Lambda optimization is one of our specialties. Get in touch if you’re seeing unexpected AWS bills.