AWS Lambda timeouts under concurrent load: fix and prevent
Lambda timeouts during concurrent execution usually come from cold starts, connection pool exhaustion, or function timeout settings. Here's the fix.
Quick answer
Increase your function's timeout setting under Configuration > General configuration > Timeout, and if you're using a database or API inside the Lambda, set a connection pool with a max of 1 connection per Lambda instance.
Why this happens
You've got a Lambda function that works fine when you test it manually. But the second you hit it with real traffic – say, 50 users uploading files at the same time – it starts throwing Task timed out after 3.00 seconds errors. This isn't a mystery. Here's what's going on:
When multiple instances of your Lambda run at the same time (that's concurrency), each one has to establish its own connections. If you're connecting to a database or an external API, those connections take time. The first few requests might complete okay, but as the pool of database connections fills up, new Lambda instances queue up waiting for a free connection. While they wait, the Lambda's timeout clock is ticking.
Another big one: cold starts. If your function hasn't run in a while (or if traffic spikes suddenly), Lambda needs to spin up new execution environments. That adds 1-3 seconds to the first request per new instance. If your timeout is set to the default of 3 seconds, that's half your budget gone before the code even runs.
One more sneaky cause: your code might be using a global variable or a singleton that's not thread-safe. When two concurrent Lambda instances try to use the same resource, they can block each other. But that's rarer – the real fix is usually below.
Step 1: Check your current timeout and concurrency settings
- Open the AWS Lambda console.
- Click on your function name.
- Go to the Configuration tab.
- Click General configuration.
- Look at the Timeout value. The default is 3 seconds, which is almost never enough for a function that does real work (database calls, file processing, external API calls).
- Also check Reserved concurrency under Concurrency. If it's set to 0, your function can't run at all. If it's unset, it can use up to the account-level limit (usually 1000).
- After you see those values, write them down. You'll change them in step 3.
Step 2: Update your code to reuse connections
This is the part most docs gloss over. Inside your Lambda handler, you should initialize database clients, HTTP clients, and SDK clients outside the handler function. That way, when Lambda reuses a warm execution environment, those connections are already there.
Here's an example for a Node.js function connecting to a MySQL database:
const mysql = require('mysql2/promise');
// Connection pool created once, reused across invocations
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 1, // One connection per Lambda instance
queueLimit: 0
});
exports.handler = async (event) => {
// Use the pool here – it's already connected
const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [event.userId]);
return rows;
};The key part: connectionLimit: 1. Each Lambda instance gets exactly one database connection. That prevents a single instance from hogging multiple connections from the database pool. If you need more throughput, increase the number of Lambda instances (concurrency) instead.
For Python (using boto3 or a database client), do the same thing – initialize the client outside the handler:
import boto3
# Client created once
s3 = boto3.client('s3')
def lambda_handler(event, context):
# Use s3 here
response = s3.list_buckets()
return responseStep 3: Increase the timeout setting
Go back to the Lambda console.
- Under Configuration > General configuration, click Edit.
- Change the Timeout value. For most functions that do database work or API calls, 10 seconds is a good starting point. If you're processing files or doing heavy computation, start with 30 seconds.
- Click Save.
- You should see the new timeout reflected in the console. No error message here – it just updates.
Don't go overboard. Max is 15 minutes (900 seconds), but anything over 5 minutes suggests you should rethink the architecture. Break the work into smaller chunks or use Step Functions.
Step 4: Enable Provisioned Concurrency (if you have consistent traffic)
If your timeouts happen during traffic spikes (like a batch job kicking off 100 Lambda instances at once), provisioned concurrency keeps a set number of environments warm. They won't cold start.
- In the Lambda console, go to Configuration > Concurrency.
- Click Add reserved concurrency and set a number. If you know you'll have 50 concurrent requests, set it to 50.
- Then, under Provisioned concurrency, click Add. Set it to the same number (or less, depending on your budget).
- Pick the alias or version you want to pre-warm (usually
$LATESTfor testing, or a specific version for production). - Click Save.
- After a minute or two, you'll see the Provisioned Concurrency status change to Ready.
Note: Provisioned concurrency costs money. You pay for the time those environments are kept warm. But if you need consistent sub-second response times under load, it's worth it.
Alternative fix: Increase database connection pool size (not recommended for Lambda)
Some people try to fix this by increasing the database's max connections. That can work if your database has headroom, but it's a band-aid. Lambda can scale to hundreds of instances in seconds. Your database might not handle that many connections. Instead, limit each Lambda to one connection (like we did above) and use a connection proxy like RDS Proxy or PgBouncer if needed.
If you're hitting an external API, add retry logic with exponential backoff. But also check if the API has a rate limit. If it does, your Lambda will keep timing out when it hits the limit. You can use aws-sdk's built-in retry mechanism for AWS APIs.
Prevention tip for the future
Set up CloudWatch alarms on the Duration metric. If your function's average duration creeps above 80% of the timeout, you'll get a warning. Also monitor ConcurrentExecutions – if it hits your reserved concurrency, you'll start seeing throttles (not timeouts). Throttles and timeouts are different problems. Throttles mean AWS won't even start the function – you get a 429 error. Timeouts mean the function started but didn't finish.
Finally, test your function under load before deploying to production. Use a simple load testing tool like hey or artillery. Simulate 50 concurrent requests and watch the logs. You'll spot the timeout pattern immediately.
That's it. Start with steps 1-3, and your concurrent execution timeouts should go away. If they don't, check if your function is in a VPC – that adds latency for ENI creation. Consider using VPC endpoints or Lambda's cloudwatch:PutMetricData without a VPC if you can.
Was this solution helpful?