Filter concepts by levelShowing all levels.

AWS · Production Debugging

Production Debugging on AWS

A search order, eleven recurring signatures, and one field that makes logs joinable

  • Order beats intuitioneach step eliminates a region
  • Eleven signaturesmetric + error + first check
  • One id, every linecrosses queues, where traces do not
  • Stop when it is explainedcontinuing produces theories, not causes

1.The Fixed Investigation Order

Under pressure, people debug by guessing at the part of the system they know best. A fixed order removes the guessing: state the symptom precisely, check what changed recently, then walk outward through metrics, logs, traces, dependencies, network, permissions and quotas. Each step either explains the symptom or eliminates a whole region of the system.

Key features

  • State the symptom as a measurement — which endpoint, what error, since when, what fraction of requests — before opening any console
  • Most incidents follow a change: a deploy, a configuration edit, a feature flag, a scaling event, or a dependency's own release
  • Metrics tell you where and when; logs tell you what; traces tell you which hop — reaching for logs first means reading the wrong service's logs
  • A trace shows application time, database time and downstream time separately, which is the question "why is it slow" actually asks
  • IAM failures are specific and loud: an AccessDenied names the principal, the action and the resource
  • A cost or usage anomaly that starts at the same minute as the symptom is evidence, not a coincidence

Each step: the question, where to look, and what a hit looks like

Each step: the question, where to look, and what a hit looks like
StepThe questionWhereA hit looks like
1 SymptomWhat exactly is failing, and since when?The report, plus a dashboardA precise statement: "POST /orders, 502, 12% since 14:05"
2 Recent changeWhat changed near that time?Deployment history, CloudTrail, Config timelineA change within minutes of the start time
3 MetricsWhere in the system, and when exactly?CloudWatch metrics and alarmsOne tier's error or latency curve matching the symptom
4 LogsWhat did the failing requests say?CloudWatch Logs, filtered by the request idA recurring error, or a handler that starts and never finishes
5 TracesWhich hop consumed the time?X-Ray or OpenTelemetry trace mapOne segment holding most of the latency
6 DependenciesIs something we depend on unhealthy?RDS, ElastiCache, queue depth, provider statusConnections at maximum, or a queue growing without draining
7 NetworkCan the packets get there?Security groups, NACLs, route tables, Flow Logs, DNSRejected connections, or a name resolving to the wrong target
8 IAMAre we allowed to do this?Error message, CloudTrailAccessDenied naming a principal, action and resource
9 QuotasHave we hit a ceiling?Service Quotas utilization, throttling metricsThrottling or a limit-exceeded error at a round number
10 CostIs spend telling us something the metrics are not?Cost Explorer, anomaly detectionA cost or usage curve that turns at the same minute
Ten steps, narrowing at every one

The funnel is the point: each step removes candidates rather than confirming a hunch. Skipping to a favourite step widens the search instead of narrowing it.

  • A funnel of ten numbered steps, each narrower than the one above.
  • Step 1, symptom: which endpoint, what error, since when, what fraction.
  • Step 2, recent change: deploys, config edits, flags, scaling events, dependency releases.
  • Step 3, metrics: where and when — error rate, latency, saturation, queue depth.
  • Step 4, logs: what the failing requests actually said.
  • Step 5, traces: which hop consumed the time.
  • Step 6, dependency health: database, cache, queue, third parties.
  • Step 7, network: security groups, NACLs, routes, DNS.
  • Step 8, IAM: AccessDenied naming principal, action and resource.
  • Step 9, quotas: throttling and limit-exceeded errors.
  • Step 10, cost and anomaly signals: an unexplained spend change at the same minute.
  • A note at the bottom: stop at the first step whose answer explains the symptom.

Signature

symptom → recent change → metrics → logs → traces → dependencies → network → IAM → quotas → cost

Symptom, recent change, metrics, logs, traces, dependencies, network, IAM, quotas, cost — in that order. Write the symptom down including what is not affected, because that line eliminates more of the system than anything else you will do, and stop as soon as the symptom is fully explained.

2.The Eleven Investigations You Will Actually Run

Production incidents on AWS repeat. The same eleven investigations account for most of them, and each has a signature — a specific metric, a specific error, a first place to look. Recognising the signature is what turns a twenty-minute search into a two-minute confirmation.

Key features

  • A NAT gateway supports up to 55,000 simultaneous connections per destination per IPv4 address; each additional IP address adds another 55,000
  • A NAT gateway connection idle for 350 seconds or more times out, and the gateway sends an RST rather than a FIN
  • The `ErrorPortAllocation` and `IdleTimeoutCount` metrics are the direct evidence for NAT port exhaustion
  • Lambda throttling is a 429 from Lambda itself and is a concurrency question, not a code question
  • An ECS task restart loop is usually a failing health check, an exhausted memory limit, or a container exiting non-zero at startup
  • AccessDenied names the principal, the action and the resource — read the message rather than guessing at the policy

Signature, first check, and the cause it usually is

Signature, first check, and the cause it usually is
InvestigationSignatureFirst checkUsual cause
5xx spikeALB HTTPCode_ELB_5XX vs HTTPCode_Target_5XXWhich of the two movedELB 5xx means no healthy target; target 5xx means the app returned it
Latency spikep95 up, error rate flatA trace map for the slow periodOne downstream hop — usually a query or a third party
Lambda throttles429 from Lambda, `Throttles` metric > 0Concurrent executions against the account quotaReserved concurrency too low, or the Region quota reached
ECS restart loopTasks stopping minutes after startingThe stopped-task reason stringFailed health check, OOM kill, or a non-zero exit at startup
EC2 CPU saturationCPUUtilization pinned, or CPU credits at zeroWhether the instance type is burstableUndersized instance, or a burstable type used for steady load
RDS connection exhaustionDatabaseConnections at max, low CPUConnections per task × task countPools not returning connections, or a scale-out multiplying pools
SQS backlogApproximateAgeOfOldestMessage climbingWhether consumers are running and succeedingWorkers crashed, scaled on the wrong metric, or poisoned by one message
NAT exhaustion / cost`ErrorPortAllocation` > 0, or a large NAT data billDestination distribution and `IdleTimeoutCount`Many connections to one destination; or S3 traffic with no gateway endpoint
DNS failureIntermittent, differs per client, resolves eventuallyThe record itself, and its TTLA record change still propagating, or resolver configuration
IAM AccessDeniedExplicit error naming action and resourceThe message, then CloudTrail for the same callA missing action, an SCP, or a resource policy
Security group issueConnection hangs then times outWhether the rule references the caller's groupA rule referencing a CIDR that changed, or a missing return path on a NACL
Eleven signatures, grouped by where they live

It is returning errors

5xx spike

ALB 5xx vs target 5xx tells you who produced it

IAM AccessDenied

the message names principal, action, resource

Security group refusal

connection times out rather than refusing

It is slow, not failing

Latency spike

a trace attributes it to one hop

RDS connection exhaustion

pool at maximum, database idle

DNS failure

partial, cache-dependent, looks intermittent

It is at a ceiling

Lambda throttles

429 from Lambda — concurrency, not code

EC2 CPU saturation

or credit exhaustion on a burstable type

NAT port exhaustion

55,000 per destination per IP

It is falling behind

SQS backlog

age of oldest message, not depth

ECS restart loop

health check, memory limit, or startup exit

  • It is returning errors
    • 5xx spike — ALB 5xx vs target 5xx tells you who produced it
    • IAM AccessDenied — the message names principal, action, resource
    • Security group refusal — connection times out rather than refusing
  • It is slow, not failing
    • Latency spike — a trace attributes it to one hop
    • RDS connection exhaustion — pool at maximum, database idle
    • DNS failure — partial, cache-dependent, looks intermittent
  • It is at a ceiling
    • Lambda throttles — 429 from Lambda — concurrency, not code
    • EC2 CPU saturation — or credit exhaustion on a burstable type
    • NAT port exhaustion — 55,000 per destination per IP
  • It is falling behind
    • SQS backlog — age of oldest message, not depth
    • ECS restart loop — health check, memory limit, or startup exit

Signature

5xx · latency · Lambda throttles · ECS restarts · CPU · RDS connections · SQS backlog · NAT ports · DNS · IAM · SG

Eleven signatures cover most AWS incidents. Split ALB 5xx from target 5xx before anything else, read the AccessDenied message rather than widening a policy, check whether consumers are succeeding before scaling a backlog, and remember that NAT allows 55,000 simultaneous connections per destination per IP address.

3.Correlation IDs and Request IDs

A correlation ID is one value attached to a request at its entry point and carried through every service, log line and downstream call it touches. It turns "a customer says checkout failed at about two o'clock" into one exact search that returns every log line from every service for that one request.

Key features

  • X-Ray propagates the trace with an `X-Amzn-Trace-Id` header carrying a root trace ID, an optional parent segment ID, and the sampling decision
  • The first X-Ray-integrated service the request reaches adds the tracing header, and downstream services read and extend it
  • X-Ray samples by default — the SDK records the first request each second and five percent of additional requests — so not every request has a trace
  • Annotations are indexed key-value pairs you can filter traces by, up to 50 per trace; metadata is not indexed and is for context you only read
  • Your correlation ID belongs in every structured log line, not only in the entry-point log, or the search only finds one service
  • Return the correlation ID to the client in the error response, so a user report arrives with the search key already attached

Which identifier answers which question

Which identifier answers which question
IdentifierCreated byScopeUse it to
Correlation IDYour entry point (API, or the client)The whole user action, including asynchronous follow-upFind every log line for one user action across every service
Trace ID (`X-Amzn-Trace-Id`)The first X-Ray-integrated service reachedOne traced request pathSee where the time went, hop by hop
AWS request IDThe AWS service being calledOne AWS API callQuote to AWS Support; correlate with CloudTrail
ALB request IDThe load balancerOne request through the ALBJoin access logs to application logs
Message ID / receipt handleSQSOne messageTrace one delivery, not one user action
One id, attached once, carried everywhere
Browser
ALB
API
Worker
CloudWatch
  1. 1POST /checkoutNo id yet — this is where one is created
  2. 2forward, with X-Amzn-Trace-IdThe edge adds the tracing header
  3. 3log {correlation_id, trace_id, user_id, route}One JSON line per event, always carrying the id
  4. 4enqueue job, id in the message attributesAsync work keeps the same correlation id
  5. 5500, body includes the correlation idThe user can now quote the search key
  6. 6log {correlation_id, …} for every stepSame id, hours later, different service
  7. 7one query returns every line for that requestAcross services, synchronous and asynchronous alike
  1. Browser → ALB: POST /checkout (No id yet — this is where one is created)
  2. ALB → API: forward, with X-Amzn-Trace-Id (The edge adds the tracing header)
  3. API → CloudWatch: log {correlation_id, trace_id, user_id, route} (One JSON line per event, always carrying the id)
  4. API → Worker: enqueue job, id in the message attributes (Async work keeps the same correlation id)
  5. API → Browser: 500, body includes the correlation id (The user can now quote the search key)
  6. Worker → CloudWatch: log {correlation_id, …} for every step (Same id, hours later, different service)
  7. CloudWatch → CloudWatch: one query returns every line for that request (Across services, synchronous and asynchronous alike)

Signature

correlation_id (yours, every line, crosses queues) · X-Amzn-Trace-Id (X-Ray, sampled) · AWS request id (one API call)

Create one correlation ID at the entry point, bind it into the logging context so every line carries it, propagate it across HTTP calls and queue messages, and return it to the client on error. Log the trace ID and the AWS request ID beside it — traces apportion time, correlation IDs find the lines, and sampling means traces alone will miss requests.

Remember

  • Write down what is NOT affected — that line eliminates more of the system than anything else in the investigation.
  • Split ALB 5xx from target 5xx first: the load balancer producing errors and the application producing them are different incidents.
  • A NAT gateway allows 55,000 simultaneous connections per destination per IP address, and exposes ErrorPortAllocation when that is reached.
  • X-Ray samples the first request each second plus 5% of the rest by default, so the request a customer asks about may have no trace at all.

Pro tip

  • Return the correlation ID in the error response. The user then reports the search key along with the complaint, and the investigation starts with one query instead of a time-window guess.
  • Before scaling a queue backlog, check the deletion rate: zero deletions means consumers are failing, and adding workers multiplies the failure instead of clearing it.

Next up

  • AWS Service Quotas and Limits
  • Monitoring — CloudWatch
  • AWS Networking at Senior Level

Sources

  • AWS X-Ray concepts — docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html
  • Troubleshoot NAT gateways — docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-troubleshooting.html
  • Checked against official AWS documentation 2026-09-04

Spotted a mistake on this page? Tell us.