AWS us-east-1 DynamoDB DNS Outage (2025)
Open in the interactive app: practice, quiz, mock interview →In one minute
- On 19 to 20 October 2025, DynamoDB in AWS's US-EAST-1 region became unreachable for about three hours (23:48 to 02:40 PDT) because its regional endpoint's DNS record was left empty (AWS summary). Many AWS services and customers depend on DynamoDB, so the impact spread widely, and some services were affected until 14:20 PDT; Redshift clusters took until 4:05 on 21 October to recover fully (AWS summary).
- DynamoDB's DNS is managed by automation: a DNS Planner produces versioned plans, and DNS Enactors in three availability zones apply them to Route 53. A race between two Enactors let a delayed one apply a much older plan over a newer one; a cleanup process then deleted that old plan, which was now the active one, removing every IP address from the endpoint.
- The automation was left in an inconsistent state that blocked further updates, so engineers had to repair DNS by hand.
- Recovery triggered second-order failures: EC2's DropletWorkflow Manager, which leases physical servers and depends on DynamoDB, went into congestive collapse when it tried to re-establish every lease at once (AWS summary); then delayed network state caused Network Load Balancer health checks to flap and remove healthy capacity.
- AWS's fixes: disable the DNS automation worldwide until the race is fixed and safeguards against applying incorrect plans are added; velocity controls on NLB failover; new tests for the EC2 recovery workflow; and throttling based on queue size.
How it works
Timeline (PDT, from AWS's summary)
- 19 Oct, 23:48 (06:48 UTC on 20 Oct) Systems connecting to DynamoDB's public endpoint in US-EAST-1 start getting DNS failures. DynamoDB API error rates rise.
- 20 Oct, 00:38 Engineers identify DynamoDB's DNS state as the source.
- 01:15 Temporary mitigations let some internal services connect and repair key internal tooling.
- 02:25 All DNS information is restored. 02:32 Global table replicas catch up. 02:40 Customer connections recover as cached DNS records expire.
- From 23:48 EC2's DropletWorkflow Manager (DWFM) cannot complete its periodic state checks, which depend on DynamoDB, so leases on physical servers ("droplets") time out. Running instances are fine, but new launches cannot use those droplets.
- 02:25 As DynamoDB returns, DWFM tries to re-establish leases for the whole fleet. The volume causes work to time out and be retried: congestive collapse. New launches fail with insufficient capacity errors.
- 04:14 Engineers throttle incoming work and selectively restart DWFM hosts; 05:28 all droplets have leases again.
- A backlog in network state propagation means new instances launch without network connectivity until about 10:36. Other services fell with their dependencies: STS and IAM sign-in recovered around 01:19 to 01:25 but STS was only fully back at 09:59; Lambda, ECS, EKS, Fargate and Amazon Connect were hit and recovered between 11:27 and 14:20; and AWS Support Center case creation was blocked until 02:40 (AWS summary). Existing EC2 instances kept running.
- From 05:30 Network Load Balancer health checks run against instances whose network state has not propagated; healthy nodes fail checks, flap, and automatic zone failover removes capacity. 09:36 engineers disable automatic NLB health check failover, restoring capacity.
- 11:23 EC2 throttles start to relax; 13:50 EC2 APIs and launches are normal. 14:09 NLB automatic failover is re-enabled. 14:20 the overall event ends.
Technical root cause
DynamoDB's DNS automation has two parts (AWS summary). The DNS Planner watches load balancer health and capacity and produces versioned DNS plans: which load balancers, with which weights, for each endpoint. DNS Enactors, running redundantly in three availability zones, apply plans to Route 53 using transactions, and a cleanup process removes plans much older than the latest applied one.
The race: one Enactor was unusually slow, retrying updates on several endpoints. Meanwhile, the Planner produced many newer plans, and a second Enactor applied the newest plan quickly, then ran cleanup. The slow Enactor, whose check that its plan was newer than the current one had been made long before, then wrote its much older plan over the regional endpoint. Cleanup deleted that old plan because it was far older than the newest applied one, but it was now the active plan. Deleting it removed all IP addresses for dynamodb.us-east-1.amazonaws.com, and the inconsistent state blocked further automatic updates.
Contributing factors
- A time-of-check to time-of-use gap. The "is my plan newer?" check was made before a long delay and not re-checked atomically at write time.
- Cleanup trusted generation numbers, not the live state. It deleted a plan that was in use.
- Shared dependency. Many AWS control planes, including EC2's server lease management, depend on DynamoDB in the region.
- Recovery at full volume. DWFM tried to restore every lease at once, and retries amplified the load into congestive collapse.
- Health checks during abnormal conditions. NLB health checks treated not-yet-propagated network state as failure, and automatic failover removed capacity.
Detection and mitigation
Failures appeared immediately; locating DNS as the source took about 50 minutes (23:48 to 00:38) (AWS summary). DNS was repaired manually. Each later stage needed its own mitigation: throttling and selective restarts for DWFM, and disabling automatic failover for NLB health checks.
What changed afterwards (per AWS)
The DynamoDB DNS Planner and Enactor automation was disabled worldwide; before re-enabling it, AWS will fix the race condition and add protections against applying incorrect DNS plans. NLB gets a velocity control limiting how much capacity a single load balancer can remove when health checks trigger zone failover (AWS summary). EC2 adds a test suite for the DWFM recovery workflow and improves throttling to rate-limit incoming work based on queue size. AWS said it would keep reviewing the event across services.
What I would have done as the tech lead
Make plan application a single atomic compare-and-set on the generation number at write time, and make cleanup refuse to delete whatever is currently active, checked against live state. Add an invariant monitor that pages when any endpoint has zero records. For dependent systems, design recovery paths that are rate-limited by default and tested at full fleet scale, and cap how much capacity any automatic health mechanism may remove at once.
Down the stack
Layer 1The contract
DynamoDB's regional endpoint promises that its DNS name always resolves to healthy load balancers. The DNS automation's implicit contract is monotonic: the endpoint only ever moves to newer plans, and no step leaves it empty. The race broke both.
Layer 2The mechanism
The bug is a classic time-of-check to time-of-use (TOCTOU) race, the same shape as check-then-act in a database. Two safe designs exist. Compare-and-set: write the plan only if the stored generation is still lower than mine, in the same transaction as the write; Route 53 change batches are all-or-nothing (Route 53 API reference), but that alone does not compare a generation, and AWS's summary does not say how the Enactors guard their writes, so treat this as a design sketch and not a description of their code. Fencing tokens: every writer carries a monotonically increasing token, and the store rejects writes with an older token, which is how Kleppmann says to protect against a paused or delayed writer. Separately, garbage collection of plans must be relative to the live state: never delete the plan currently referenced by any endpoint, whatever its age.
Layer 3The machine
DNS failure spreads through caching. Resolvers and clients cache answers for the record's TTL; when the record became empty, clients that refreshed got no addresses, while some cached good answers until expiry. Recovery works the same way in reverse: after DNS was restored at 02:25, connections recovered over the following minutes as caches expired (02:40). The congestive collapse in DWFM is a queueing phenomenon: when work arrives faster than it can be completed within its timeout, timed-out work is retried, adding load, so useful throughput falls towards zero even though machines are busy. Throttling admission below capacity is the way out (AWS Builders' Library on load shedding), which is what the engineers did at 04:14 and what AWS said it would build into EC2's propagation path as queue-size-based rate limiting. This is the metastable failure pattern described by Bronson et al.: a trigger is gone, but a feedback loop keeps the system down until load is removed.
Why it's built this way
- DNS automation must react quickly to load balancer health, so it runs continuously and redundantly.
- Redundant writers need atomic, generation-checked updates, or they race.
- Cleanup must respect live state, or it can delete what is in use.
- Shared dependencies turn one service's failure into many, so dependents need graceful degradation.
- Recovery itself can overload systems, so it must be throttled and rehearsed.
Diagram
sequenceDiagram participant P as DNS Planner participant A as Enactor A (delayed) participant B as Enactor B participant R as Route 53 endpoint P->>A: plan gen 100 A->>A: check: 100 newer than current? yes P->>B: plans up to gen 140 B->>R: apply gen 140 A->>R: apply gen 100 (stale check) B->>B: cleanup plans far older than 140 B->>R: delete gen 100 (now active) Note over R: endpoint has no IP addresses
Worked example
A platform team runs a config distributor: three replicas pull versioned routing configs from a planner and write them into a shared key-value store that edge proxies read. Each replica does current = get(); if new.version > current.version: put(new), and a janitor deletes configs more than 50 versions behind the newest.
After reading AWS's summary, they reproduce the race in a test: replica A reads version 200, pauses (simulating a long GC pause), replica B writes 260, the janitor deletes everything below 210, then A writes 200. The proxies now point at a deleted config and fall back to an empty route table.
Fixes: put becomes a conditional write (put_if(version_greater_than_stored)), executed atomically by the store; the janitor reads which version is live and never deletes it or anything newer; an invariant check alerts within 30 seconds if any proxy reads an empty or missing config; and proxies keep serving their last good config if the new one is missing (fail static). The reproduction test now ends with version 260 live, and it runs in CI.
In the wild
- Google Cloud, 2025: a blank policy row and a retry herd. A quota policy with blank fields replicated globally within seconds and crashed Service Control, which had no feature flag or error handling on the new path. When tasks restarted in us-central1 without randomized backoff, they overloaded the Spanner table behind it, so that region recovered more slowly than the rest. Google planned fail-open checks and incremental propagation of global data (Google Cloud status).
- AWS, 2021: a connection storm in us-east-1. An automated scaling action made internal clients open a surge of connections that overwhelmed the devices between AWS's internal and main networks. A latent bug stopped clients backing off, so retries sustained the congestion, and monitoring on the same network degraded too; recovery took roughly seven hours (AWS summary).
- Slack, 2022: cache churn turned into metastable overload. A rollout restarted agents one by one, which swapped empty cache nodes in and dropped hit rates. Misses on a query that scattered across every database shard overloaded the databases, and the overload outlasted the trigger (Slack Engineering).
Trade-offs
| Design | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| Check then write | Simple | Races with concurrent or delayed writers | Single writer only |
| Atomic compare-and-set on generation | No stale overwrite | Needs store support | Multiple writers to shared state |
| Fencing tokens | Robust to pauses and partitions | Store must check tokens | Leases, leaders, delayed workers |
| Cleanup by age only | Simple | Can delete live data | Never for referenced data |
| Cleanup relative to live references | Safe | Must read live state | Garbage collection of configs or plans |
Pitfalls in production
- Redundant automation without coordination. Fix: atomic conditional writes or fencing tokens for shared targets.
- Garbage collection that ignores what is in use. Fix: compute live references first.
- No invariant alert for "endpoint has zero targets". Fix: monitor invariants, not just errors.
- Recovery that restores everything at once. Fix: admission control and rate limits built into recovery paths.
- Automatic health-based removal without limits. Fix: velocity controls and fail-static behaviour.
Leadership lens
- Review every redundant automation writing to shared state for check-then-act races; add conditional writes and race tests.
- Identify your hidden hard dependencies (for many teams, one regional database or DNS) and design degraded modes.
- Test recovery at full scale: what happens when every client reconnects in the same second?
- Cap the capacity any automated system can remove per minute, everywhere.
Check yourself
What made the delayed Enactor's write dangerous?
Show answer
Its "is this newer?" check was made long before its write, so by the time it wrote, a much newer plan was active and it overwrote it with an old one.
Why did cleanup make the situation worse?
Show answer
It deleted plans much older than the newest applied one, without checking which plan was actually active, so it deleted the active plan and left the endpoint empty.
What is congestive collapse, and how was it stopped?
Show answer
When work arrives faster than it can finish within its timeout, timeouts and retries add load until throughput collapses; engineers throttled incoming work and restarted hosts to drain queues.
Why did NLB health checks remove healthy capacity?
Show answer
New instances' network state had not propagated, so health checks failed for healthy nodes and automatic zone failover removed them from service.
Sources and further study
Primary
- Summary of the Amazon DynamoDB Service Disruption in US-EAST-1 (AWS): the source for the timeline, root cause and fixes.
- Metastable Failures in Distributed Systems (Bronson et al., HotOS 2021): why recovery can be harder than the original failure.
- How to do distributed locking (Martin Kleppmann): fencing tokens for writers that may be delayed.
Systems docs
- ChangeResourceRecordSets (Route 53 API reference): transactional change batches and propagation.
- Timeouts, retries, and backoff with jitter (AWS Builders' Library): why retries amplify load.
Books
- Site Reliability Engineering (Beyer et al.), Ch 22, Addressing Cascading Failures: overload, retries and slow recovery.
- Release It! (Nygard, 2nd ed.), Ch 4 Stability Antipatterns and Ch 5 Stability Patterns: cascading failures, timeouts and circuit breakers.
Other curricula
- Azure Architecture Center: Retry pattern: bounded, backed-off retries.
Free resources
- Time-of-check to time-of-use (Wikipedia): the race pattern behind the outage.
- Using load shedding to avoid overload (AWS Builders' Library): admission control and congestive collapse.
- Handling Overload (Google SRE book): per-client limits and graceful degradation.