Filter concepts by levelShowing all levels.

AWS · Section 60

AWS Service Quotas and Limits

Level
advanced
Read
30 min
Concepts
3

AWS looks unlimited and is not: every service defines maximum values for its resources, actions and items, and every account starts at a default for each one. The vocabulary matters because it separates two very different situations. A default is where AWS starts you; an applied quota is the value actually enforced after any increase, which is why capacity planning reads the account rather than the documentation; utilization is usage as a percentage of the quota, and it is the number worth alarming on because it survives every increase. Quotas may be per account or per Region, and global quotas can only be raised from US East (N. Virginia) in the standard partition. Some are adjustable and some are not, and the console says which. That split decides everything: an adjustable quota is a request to file weeks early — increases are reviewed and may be approved, denied or partially approved, which makes them useless as a mitigation during an incident — while a fixed limit is a design input. Lambda's 900-second timeout and 6 MB synchronous payload cannot be raised, so work that exceeds them belongs on ECS, Batch or Step Functions and data that exceeds them travels as an S3 reference. NAT allows 55,000 simultaneous connections per destination per IP address. Subnet CIDRs are chosen once and bound the fleet for the VPC's life. And some limits are not granted at all but derived: total database connections is pool size times process count, an arithmetic result you compute and stay under, and the scale-out that saves the web tier is often what takes the database down.

What is true here

  1. The applied quota, not the documented default, is what is enforced in your account.
  2. Alarm on utilization at around 70%, because a review takes weeks and an incident does not wait.
  3. Adjustable limits are a request; fixed limits are design inputs no case will move.
  4. Structural limits — subnet CIDRs, key design, Region — are chosen once and changed painfully.
  5. Derived limits like total database connections are arithmetic you own.

What you will be able to do

  • Read the applied quota for a service instead of assuming the default
  • Set up utilization alarms with enough runway for an increase to be approved
  • Split a workload's limits into adjustable, fixed, structural and derived
  • Recognise when a limit means redesign rather than a support case
  • Compute the derived limits your own architecture creates
Three things to do about a quota, in order
adjustable →request earlyfixed → shapesthe designtrends reveal thenext constraint

Know the applied value

default, adjustable, applied, global

Alarm on utilization

~70%, with throttling as the backstop

Design with the fixed ones

timeouts, payloads, subnet sizes, connections

  • Know the applied value — default, adjustable, applied, global
    • leads to Alarm on utilization (adjustable → request early)
    • leads to Design with the fixed ones (fixed → shapes the design)
  • Alarm on utilization — ~70%, with throttling as the backstop
    • leads to Design with the fixed ones (trends reveal the next constraint)
  • Design with the fixed ones — timeouts, payloads, subnet sizes, connections

AWS Service Quotas and Limits

The quota vocabulary and where the real value lives, monitoring utilization early enough to act, and the fixed limits that decide the shape of a design.

Every Service Has Quotas

coreintermediate

AWS looks unlimited and is not. Every service defines maximum values for its resources, actions and items, and your account starts at a default for each one. Some of those defaults can be raised on request; others cannot be changed at all. Knowing which is which for the services you use is the difference between a capacity problem you planned for and an outage.

Think of it as

Treat a quota like a fuse rather than a wall. It exists to protect your account and other customers from a runaway workload, so hitting one is usually a signal that something is growing faster than expected — which is worth knowing whether or not you raise the limit.

What we're doing: Find out what a new account can actually run before promising a launch date.

account-readiness.txttext
The plan: launch in three weeks, expecting 5,000 requests/sec at
peak, on a brand new production account created last month.

Nobody has looked at a quota. Every number below is a default.

WHAT THE DEFAULTS ALLOW
  Lambda concurrent executions   1,000 per Region  (adjustable)
  API Gateway throttle          10,000 rps        (adjustable)
  NAT gateways per AZ                5            (adjustable)
  Elastic IPs per Region             5            (adjustable)
  Lambda function timeout          900 s          (NOT adjustable)
  Lambda sync payload                6 MB         (NOT adjustable)

THE TWO CATEGORIES, AND THE TWO DIFFERENT PLANS
  Adjustable → request the increase now, not in week three.
    Increases are reviewed and may be approved, denied, or
    partially approved, and reviews take time.

  Not adjustable → these are design inputs. No request will move
    the 15-minute Lambda ceiling or the 6 MB payload, so any
    workload that needs more must be shaped differently.

New accounts also start with reduced Lambda concurrency and
memory quotas that AWS raises automatically based on usage — so
a load test on a fresh account measures the account, not the code.
1
A new account is the worst case for quotas, and launches are usually the first time anyone runs real traffic through one.
6
Splitting the list into adjustable and not is the whole exercise: one column is a ticket, the other is a design change.
14
Requesting early costs nothing and removes the possibility of a three-week wait becoming a launch blocker.

Why this works: Quota problems are almost never discovered gradually — they appear at the exact moment traffic is highest and attention is lowest. Reading the defaults for the services in your design takes an afternoon, and it converts a class of launch-day surprise into either a request submitted early or a design decision made deliberately.

Load testing on a fresh account and trusting the number

Wrong

text
# Ran a load test in the new prod account: throttled at ~200
# concurrent. "The application does not scale."

Better

text
# Check the applied quotas for the account first.
# New accounts have reduced concurrency and memory quotas that
# AWS raises automatically as usage grows.

What you see: Weeks are spent optimising an application that was never the constraint, and the same code performs fine in the older staging account.

Why: AWS assigns new accounts a reduced quota profile intended for exploration, and raises it based on usage. A load test therefore measures the account's current quota profile as much as the workload, and comparing results between accounts of different ages compares two different ceilings.

Default, applied, and the ceiling you cannot move

Usage is what you consume; utilization is that as a percentage of the quota. The default is a starting point, the applied quota is where you have raised it to, and a hard quota cannot be moved at all.

  • A horizontal bar chart showing quota vocabulary.
  • The first bar shows usage, the number of resources or operations currently in use, filling part of the quota.
  • A marker at 75 percent of the bar is labelled utilization, defined as usage divided by the quota value; the example given is 150 resources in use against a quota of 200.
  • The end of the first bar is labelled default value, the initial quota established by AWS.
  • A second, longer bar shows the applied quota — the updated value after an approved increase — which extends beyond the default.
  • A third mark shows a hard quota, which cannot be increased at all and must be designed around.

The vocabulary, and why each term matters

The vocabulary, and why each term matters
TermMeaningWhy you care
Service quotaThe maximum resources or operations for an account or RegionIt is the real ceiling, whatever the marketing says
Default valueThe initial quota value AWS establishesWhere a brand new account starts — often far below production need
Adjustable valueA quota value that can be increasedDetermines whether the fix is a request or a redesign
Applied quotaThe updated value after an increaseWhat is actually enforced today — check it, do not assume the default
UsageResources or operations currently in useThe numerator
UtilizationUsage as a percentage of the quotaThe number worth alarming on
Global quotaApplied at account level, present in all RegionsIncreases are requested only from us-east-1 in the standard partition

Together

text
# Read the current applied quota, not the documented default
aws service-quotas get-service-quota \
  --service-code lambda \
  --quota-code L-B99A9384          # Concurrent executions

# List everything you have already had raised in this account
aws service-quotas list-requested-service-quota-change-history \
  --status CASE_CLOSED

Remember: Every service sets quotas, per account or per Region, and your account starts at a default that may already have been overridden. Split every quota you depend on into adjustable and not: adjustable ones are a request to file early, and non-adjustable ones are design inputs that no request will move.

See also: monitoring quota utilization · limits as architecture inputs · quotas as reliability constraints · inspecting apis and quotas

Monitoring Utilization and Requesting Increases Early

coreadvanced

A quota you are not watching is a quota you will discover by hitting it. Service Quotas shows utilization — usage as a percentage of the quota — and supports CloudWatch alarms on quotas that report it. An alarm at seventy percent gives you the weeks a review needs; no alarm gives you an incident.

Think of it as

Alarm on the ratio, not the raw count. Absolute numbers change when a quota is raised, so an alarm on "800 concurrent executions" becomes meaningless the day the quota moves to 5,000. Utilization stays meaningful across every increase.

What we're doing: Build the quota watch for one workload, so no limit is discovered by hitting it.

quota-watch.txttext
STEP 1 — list the quotas this workload actually consumes
  Not every quota. The ones on the request path and the ones
  that grow with customers:
    Lambda concurrent executions      per Region
    API Gateway throttle rate         per Region
    RDS max_connections               per instance class
    Elastic IPs, NAT gateways per AZ  per Region
    SQS in-flight messages            per queue

STEP 2 — record the APPLIED value for each, not the default
  get-service-quota per quota code, into the runbook. This is
  the number capacity planning uses.

STEP 3 — alarm on utilization where the quota supports it
  Threshold 70%. Notification to the team that owns the service,
  not to a shared mailbox nobody reads.

STEP 4 — alarm on throttling as the backstop
  Lambda Throttles > 0, API Gateway 429 count > 0. These fire
  when step 3 was missed or when a spike outran the trend.

STEP 5 — review quarterly, and after any growth event
  A quota raised for last year's peak is not automatically right
  for this year's. Recheck the applied values, not the defaults.
1
Scoping to the quotas this workload actually consumes keeps the list short enough to maintain — a list of every quota gets abandoned.
8
The applied value is the real number; writing it in the runbook stops planning from drifting back to the documented default.
16
Routing to the owning team is what makes the alarm actionable — a shared mailbox alarm is a deferred incident.
22
Quotas are raised for a moment in time; growth makes the review a recurring task, not a one-off.

Why this works: The gap between "approaching a quota" and "throttled by a quota" is the only window in which the cheap fix — a request — is available. Utilization alarms create that window deliberately; throttling alarms only tell you it has closed. Having both means neither a slow trend nor a sudden spike goes unnoticed.

Requesting the increase during the incident

Wrong

text
# 21:40 Friday: Lambda throttling at peak.
# 21:45: submit a quota increase request and wait.

Better

text
# Request when utilization crosses 70%, weeks earlier.
# During the incident, shed load or shift traffic instead —
# the quota is not going to move tonight.

What you see: The incident lasts as long as the review does, and the only available mitigations are the ones nobody prepared.

Why: AWS states that increases take time to review, process and approve, and may be approved, denied or partially approved. That makes a request an unusable mitigation during an incident. The mitigations that do work under time pressure — shedding load, throttling a low-value caller, shifting to another Region — have to already exist.

Where the alarm goes, and how much runway it buys

Alarm on utilization rather than on an absolute count, and set the threshold so the remaining runway is longer than a quota review takes.

  • A line chart of quota utilization over eight weeks, rising steadily.
  • A horizontal line at 100 percent marks the quota — where requests begin to be throttled.
  • A dashed line at 70 percent marks the alarm threshold.
  • The rising usage line crosses 70 percent in week five and would reach 100 percent in week eight.
  • The gap between the two crossings is labelled "runway: three weeks to request, review and receive an increase".
  • A note reads: an alarm on an absolute count stops being meaningful the moment the quota is raised, which is why the threshold is a percentage.

What to watch, and where each signal comes from

What to watch, and where each signal comes from
SignalSourceWhat it tells you
Quota utilizationService Quotas console and CloudWatch alarmsHow close you are, as a percentage that survives increases
Throttling metricsThe service's own metrics — for example Lambda `Throttles`You already crossed a limit; this is a symptom, not a warning
Error codes in logsApplication logs and CloudTrail`ThrottlingException`, `LimitExceededException`, `TooManyRequestsException`
Applied quota values`get-service-quota` per quota codeThe value actually enforced for this account today
Increase history`list-requested-service-quota-change-history`What has been raised before, and how long approvals took
Automatic managementService Quotas Automatic ManagementAWS monitoring usage and notifying you before you run out

Together

text
# The distinction that decides whether you find out early or late
Service Quotas utilization alarm  → fires at 70%, weeks of runway
Lambda "Throttles" metric alarm   → fires at 100%, zero runway

# Both are worth having. Only the first one is a warning; the
# second one is an incident notification.

Remember: Alarm on utilization at around seventy percent so there is runway for a review, and keep a throttling alarm as the backstop for spikes. Record the applied quota rather than the documented default, request increases weeks early, and re-check after every growth event — a quota is never a mitigation during an incident.

See also: every service has quotas · limits as architecture inputs · alarms dashboards and event driven actions · quotas as reliability constraints

Limits Are Architecture Inputs

coreadvanced

Some limits are numbers to raise; others are constraints that decide the shape of the design. A 15-minute function ceiling is not a capacity problem — it is a statement about what kind of work belongs in a function. Reading those constraints before drawing the architecture is cheaper than discovering them after.

Think of it as

Before choosing a service, ask what it will not do. Then check whether your workload sits inside that answer. Designs fail late because this question is asked in the wrong order: the service is chosen first, and the limit is met months later when changing course is expensive.

What we're doing: Catch a limit at design time instead of at 3am four months later.

design-review-limits.txttext
Proposal: "Generate customer PDF exports in a Lambda function
triggered by an API call. Return the PDF in the response."

Three limits contradict this, and all three are non-adjustable.

1. TIMEOUT — 900 seconds maximum.
   The largest customer's export takes about 6 minutes today and
   grows with their data. This is not a problem yet, which is
   exactly what makes it dangerous: it fails first for the
   biggest, most valuable customer, and only in production.

2. PAYLOAD — 6 MB synchronous response.
   A 40 MB PDF cannot be returned this way at all. Not slowly:
   not at all. No quota increase exists for this.

3. CONCURRENCY — 1,000 per Region by default.
   Every export holds an execution environment for minutes, so a
   few hundred concurrent exports exhaust the account's Lambda
   capacity — including the functions doing unrelated work.

THE REDESIGN, which the limits wrote for us
   POST /exports          → Lambda, ~50 ms: create a job, enqueue,
                            return 202 with a job id
   SQS → ECS task         → renders with no time limit
                          → writes the PDF to S3
   GET /exports/{id}      → 200 with a presigned S3 URL when ready

Same services, different shape. The function does the fast part,
the container does the slow part, S3 carries the bytes.
1
A proposal that sounds reasonable and is blocked by three separate hard limits — none of which are visible in the sentence.
5
"Not a problem yet" is the most expensive kind of limit problem, because it surfaces on the largest customer first.
13
A payload ceiling is qualitatively different from a slow response: there is no version of this design that works.
20
The redesign is not a compromise — it is the shape the limits were pointing at, and it is better on every axis.

Why this works: Non-adjustable limits are the most useful design input AWS publishes, because they rule options out early and for free. The alternative is discovering them from production behaviour, at which point the fix is the same redesign plus a migration, an incident, and the customers who hit it first.

Sizing a subnet for the fleet you have today

Wrong

text
# Three /24 private subnets: 251 usable addresses each
# (AWS reserves 5 per subnet). "Plenty — we run 20 tasks."

Better

text
# /20 private subnets: ~4,091 usable addresses each.
# Address space inside a planned VPC CIDR costs nothing unused.

What you see: A scale-out or a deployment fails with insufficient free addresses, and the fix is renumbering a live VPC — which is a migration, not a change.

Why: Every task, pod, interface endpoint and load balancer node consumes addresses from its subnet, and awsvpc-mode tasks consume one each. Subnets cannot be resized after creation, so the choice made in the first week bounds the fleet for the life of the VPC — and unused address space inside a well-planned CIDR range costs nothing at all.

Raise it, or design around it

Adjustable — file a request

Lambda concurrency, API Gateway throughput, Elastic IPs, NAT gateways per AZ, VPCs per Region. Plan weeks ahead, not hours.

Fixed — a design input

Lambda 15-minute timeout, 6 MB synchronous payload, SQS 256 KB message. No request moves these; the architecture accommodates them.

Structural — chosen once, changed painfully

Subnet CIDR sizing, partition key design, Region selection. Technically changeable, practically a migration.

Derived — a consequence of your own choices

Connections per database = pool size × processes. Nobody grants this; you compute it and stay under it.

  1. Adjustable — file a request — Lambda concurrency, API Gateway throughput, Elastic IPs, NAT gateways per AZ, VPCs per Region. Plan weeks ahead, not hours.
  2. Fixed — a design input — Lambda 15-minute timeout, 6 MB synchronous payload, SQS 256 KB message. No request moves these; the architecture accommodates them.
  3. Structural — chosen once, changed painfully — Subnet CIDR sizing, partition key design, Region selection. Technically changeable, practically a migration.
  4. Derived — a consequence of your own choices — Connections per database = pool size × processes. Nobody grants this; you compute it and stay under it.

The limits that shape designs, and what each one forces

The limits that shape designs, and what each one forces
LimitValueWhat it forces
Lambda timeout900 seconds (15 minutes), not adjustableLong jobs go to ECS, Batch or Step Functions — not a bigger function
Lambda payload6 MB synchronous each way, 1 MB asynchronousPass an S3 key, never the object
Lambda concurrency1,000 per Region by default, adjustableA queue between the front door and the function, or a matching increase
API Gateway throughput10,000 rps by default, adjustableCheck it against downstream concurrency — the two defaults do not match
NAT connections55,000 per destination per IP addressConnection reuse, extra IPs, or a VPC endpoint that avoids NAT entirely
Subnet addressesFixed by the CIDR you chose at creationSize subnets for the fleet you will have, not the one you start with
Database connectionsBounded by instance classA bounded pool, and RDS Proxy when many short-lived processes connect
SQS message size256 KB per messageLarge payloads by reference — the message points at S3
S3 object size5 TB maximum object, 5 GB maximum single PUTMultipart upload above 5 GB — a client feature, not a request

Together

text
# The derived limit nobody grants you — compute it before you scale
RDS db.r6g.large      max_connections ≈ 1,000
ECS tasks at peak                        40
pool size per task                       20
                                     ─────────
total at peak                           800   ← 80% of the ceiling

# Now add the worker service, the migration task and a
# reporting tool, and the scale-out that saves the web tier is
# the event that takes the database down.

Remember: Sort every limit into adjustable, fixed, structural, or derived. Adjustable ones are a request filed early; fixed ones such as the 15-minute Lambda timeout and the 6 MB payload write the architecture for you; structural ones like subnet sizing are chosen once; and derived ones like total database connections are yours to compute and stay under.

See also: every service has quotas · monitoring quota utilization · subnet cidr planning · serverless operational limits

Advertisement