Platform Engineering System Design

Serverless Limits: Timeouts, Memory, Concurrency, and When Not to Use Serverless

Serverless Limits on time, memory, and concurrency shape every design. Learn the hard caps, the cost traps, and when a long-running service is the safer choice.

Executive Summary: A serverless platform will stop your function even when the work is correct — a timeout, a memory cap, or a concurrency quota hits mid-execution, and a naive retry against a tightening quota just makes the throttling worse. This guide covers designing around hard timeout and memory ceilings, concurrency limits that throttle under a real traffic spike, and the point where the economics and constraints flip and a long-running service becomes the safer, cheaper choice.

Serverless Limits matter because the platform will stop your function even when the work is correct. When a timeout, a memory cap, or a concurrency quota hits, the call fails or waits. You then retry, and the retry can make the quota tighter. A design that ignores the cap will melt on the first real peak.

A limit is a hard or soft ceiling on time, RAM, payload, disk, or how many copies may run. Some ceilings are per function. Some are per account and per region.

You can raise a few by asking. You cannot raise the ones the platform treats as fixed. Read the current quota page before you promise a behavior the cap forbids.

In my experience, teams learn the timeout on a quiet test and miss the account concurrency cap. Still, the cap is what bites on launch day. If you share a region quota with other apps, their peak is your outage. You should budget concurrency the way you budget CPU on a cluster.

This guide shows the limits that change design. It also shows when a long-running service is the better host. Then it covers trade-offs, failure modes, and what the caps cost at scale.

What the limits are and why they fail in production

Timeouts bound how long one invoke may run. Memory bounds the RAM for that copy, and on some platforms it also sets CPU. Concurrency bounds how many copies run at once.

Payload and temp disk bound what you can pass in and what you can store locally. The Lambda quotas page lists the current numbers for that platform.

Production fails when a cap arrives as a user error. First, a timeout kills a call that would have finished a second later. The client retries.

Health of warm copies still looks fine, because the next short call succeeds. After the retry storm, concurrency is gone and new calls get throttled.

A second failure is an out-of-memory kill on a large payload. The function is correct on a small test object. A real file crosses the cap. As a result, you drop the one request you cared about and the log shows a kill, not a stack trace from your code.

A third failure is account concurrency. One busy function takes the regional pool. Other functions in the same account stall.

That stall is tail latency for every app that shares the quota. A per-function reserved cap would have contained it.

Cold paths make limits feel worse. Init time counts against the timeout on many setups. If the first call is cold, you have less time left for the handler. Read about cold starts before you set the timeout to the warm-path number.

How to design inside the caps

Time, memory, and payload

Split work so one invoke stays well under the timeout. A request path should finish with time left for a cold start and a slow dependency. A batch path should checkpoint and exit, then start again, instead of running for the max window. When the job needs hours, a function is the wrong host.

Size memory for the peak object, not the average. A cap that fits the test file will kill the real one. Also remember that a higher memory setting can add CPU and raise cost.

Test two sizes. Keep the smallest that stays under the cap at peak with headroom.

Keep payloads small. Pass an id, and let the function read the blob from storage. A huge inline body hits the payload cap and also slows the call.

If you must accept a large upload, send it straight to storage. The function should see a pointer, not the bytes.

Concurrency and shared quotas

Treat concurrency as a budget. The Lambda concurrency docs explain account quotas and reserved concurrency. Reserve a ceiling for the noisy function so it cannot eat the region. Reserve a floor for the critical one so a neighbor cannot starve it.

Downstream systems have limits too. A function can scale faster than a database will allow. If you open a new connection per invoke, the database dies first.

Cap concurrency on purpose. Reuse connections in the warm process, and still bound the pool.

The Lambda runtime environment is frozen between calls and reused when warm. State in global memory can leak across calls if you are careless. It can also help if you cache a client.

Do not store user data in that global space. The next caller may share the same copy.

Temp disk is small and local to one copy. Do not use it as a queue or a shared cache. If the copy dies, the files die.

Put durable state in a store that outlives the invoke. Use temp space only for a scratch file you can rebuild.

Trade-offs and when to leave

Serverless fits spiky, short, stateless work. You pay per use and you avoid a fleet. It fits poorly when you need long connections, a big local cache, or a steady high rate that would be cheaper on a small service. A long-running service costs more when idle and less when the rate never drops.

Reserved concurrency protects neighbors and can throttle you on purpose. That throttle is better than a region-wide stall. Still, a reserve that is too low becomes your outage. Set it from a real peak, then leave room for a retry budget.

Limit.What breaks.Safer design.Leave serverless when.
Timeout.The call dies mid-work.Checkpoint and retry the rest.The job must run for hours in one process.
Memory.The platform kills the copy.Stream the object and cap the size.You need a large in-process cache.
Concurrency.Throttles and retry storms.Reserve a cap per function.You need steady high rate all day.
Payload and disk.Upload or scratch fails.Pass an id and use object storage.You need a big local disk for the job.

Do not put a websocket fan-out or a long poll inside a short function timeout. The cap will cut the session. A small always-on service is the honest host for that shape. Use functions for the bursty bits beside it.

Pitfalls and failure modes

Most serverless incidents are limit incidents. You can time out a retry loop. You can also scale out until a database falls over. Read this list before you pick a function for a new path.

  • Setting the timeout from a warm test with no init time.
  • Sharing one regional quota across unrelated apps.
  • Retrying a timeout with no cap and no jitter.
  • Opening a new database connection on every cold start.
  • Storing user state in global memory that the next call reuses.
  • Assuming temp disk survives the next invoke on another copy.

A common mistake I have seen is a retry that ignores throttle errors. The client backs off too little, or not at all. The quota stays full and good calls wait behind retries.

Back off with jitter. Stop when the error is a timeout you already spent.

  1. List timeout, memory, payload, disk, and concurrency for the path.
  2. Mark which caps are fixed and which you can raise.
  3. Reserve concurrency for the noisy and the critical functions.
  4. Load-test above the warm pool, not only on one warm copy.
  5. Watch throttles, duration, and memory peak before you launch.

A cap you can enforce

The snippet below is a small client policy. It stops after a short budget and it backs off when the platform says throttle. Then it fails the call, because another immediate retry would only deepen the quota hole.

func callWithBudget(ctx context.Context, fn func() error) error {
    var err error
    for attempt := 0; attempt < 3; attempt++ {
        err = fn()
        if err == nil || !isThrottle(err) {
            return err
        }
        // Jitter so many callers do not retry together.
        time.Sleep(backoff(attempt))
    }
    return err
}

Pair that client with a reserved concurrency cap on the function. The reserve is the server-side stop. The backoff is the client-side stop.

After both exist, raise the account quota only if the real peak still needs it. A higher quota without a reserve just moves the stampede.

Log timeout, out-of-memory, and throttle as different reasons. One blended error rate hides which cap you hit. When the reason is memory, a timeout change will not help. When the reason is throttle, more memory will not help either.

Performance, scale, and cost

Cost follows duration, memory, and how many copies you run. A higher memory setting bills more per millisecond. In an illustrative production range, a short function at a modest size is cheap.

A function that sits near the max timeout, on a large memory setting, at high concurrency, can cost more than a small service. You should compare that bill before you commit.

Users feel throttles as P99 latency or as errors. A call that waits for a free copy looks slow. A call that gives up looks failed.

When retries pile up, both get worse. Therefore, a concurrency reserve and a retry cap belong in the design, not in the postmortem.

Autoscaling on a long-running service is the alternative when the rate is steady. You pay for idle capacity and you avoid cold caps and short timeouts. Use it when the work does not fit a limit, or when the per-call price is worse. Use functions when the work is spiky and short.

Scale has a blast radius. One function can consume a regional quota and stall the account. Isolate noisy jobs with a hard reserve.

Put critical paths in their own reserve. If two teams share a quota with no reserve, you do not have isolation.

At large scale, watch the downstream, not only the function. A concurrency cap of a few hundred can still be too high for one database. Set the function cap from the dependency, not from the platform maximum. The platform max is a ceiling, not a target.

Alert on throttle count, on timeout rate, and on memory near the cap. Also alert when duration climbs toward the timeout. That early signal lets you split the work before the kill starts. Split those pages so a memory kill does not look like a latency bug.

Key Takeaways

  • Timeouts, memory, payload, disk, and concurrency are design inputs, not surprises.
  • Account concurrency is shared. Reserve a cap so one function cannot stall the rest.
  • Pass object ids, not huge bodies. Keep temp disk as scratch you can rebuild.
  • Count init time inside the timeout. A warm test will lie about the budget.
  • Cap retries with jitter. A timeout storm will fill the quota.
  • Leave serverless when the job is long, stateful, or cheaper on a steady service.

FAQ

Can you raise every limit?

You can raise some account quotas, such as regional concurrency, by request. Fixed caps, such as the max timeout or the payload size, stay in place. Check the quota page for which is which. Design the path so a fixed cap is never on the critical line.

Does more memory always help?

It helps when you are near the cap or when extra CPU shortens a CPU-bound call. It costs more per millisecond. If the call is waiting on a database, extra memory will not fix the wait. Raise it for a measured reason, then stop.

Should retries be infinite?

They should not. A timeout or a throttle with endless retries fills the quota and blocks healthy calls. Retry a few times with jitter, then fail. Let a queue or a user action start the work again later.

When is a normal service the better host?

Choose it for long jobs, long-lived connections, large local caches, or a steady rate that never scales to zero. Functions still fit the spiky edges around that service. Do not force a shape the timeout will cut in half.

Write down the timeout, memory, payload, and concurrency caps for your next function before you code the handler. Reserve concurrency so it cannot eat the account, and cap retries with jitter. If the job cannot live inside those caps, put it on a long-running service. Then alert on throttle, timeout, and memory peak so the next limit shows up as a clear page.

Last updated on 18 September 2026.

Share this article

Leave a Reply

Your email address will not be published. Required fields are marked *