ECS Early Success Criteria: where your deployment rollback ends
Listen to article
Fernando's voiceFernando · 16:23
Powered by Amazon Polly + OmniVoice
On September 4, 2026, Amazon ECS started letting you declare when a rolling deployment is successful, using a healthyPercent over the desired count. This is not a faster-deploy button — it is the definition of where the circuit breaker and alarm-based rollback stop protecting you. This briefing covers the arithmetic of the parameter, the two source-revision cleanup modes, and what your dashboard starts hiding.
The question Amazon ECS just handed back to you is not how long your deployment takes — it is the point past which you accept having no automatic rollback. Since September 4, 2026, earlySuccessCriteria lets you close a rolling deployment once a fraction of tasks is healthy, and the documentation says out loud what that costs: after the deployment completes, the circuit breaker and alarm-based rollback no longer roll the service back.
The three numbers that define the feature
healthyPercent 90 and a desired count of 100. The remaining ten launch through regular service scaling, outside the deployment lifecycle.IN_PROGRESS → SUCCESSFUL. Nothing in EventBridge or CloudTrail tells an early success apart from a full one.The signal: the deployment stopped being a binary contract
Until now, an ECS rolling deployment only completed when four things were true at once: the target revision reached 100% of the desired count with every task running and healthy, neither the circuit breaker nor a CloudWatch alarm triggered a rollback, the alarm bake time elapsed, and the source revision tasks were cleaned up. That is an all-or-nothing contract, designed for a homogeneous fleet that launches in seconds.
earlySuccessCriteria, available for the ROLLING strategy in all commercial Regions and GovCloud (US), splits that contract in two. You declare a healthyPercent — the fraction of the desired count that must be running and healthy on the target revision — and ECS completes the deployment there. With a desired count of 100 and healthyPercent 90, the deployment turns SUCCESSFUL at task 90; the remaining ten launch afterwards through regular service scaling.
The motivating case is easy to recognize: GPU-accelerated inference. The tail of a fleet on specialized capacity does not launch at the speed of its head — the variable is hardware availability, not your container. The deployment stays open waiting for two tasks that depend on instance supply, the pipeline behind it queues, and the next deploy never starts. The same applies to the other side of the trade: services with long-lived connections, whose source revision is slow to drain and holds the deployment open for a reason that has nothing to do with the health of the new code.
Where protection ends: the deployment timeline
The same deployment, split by the success boundary. Left of it everything is reversible; right of it nothing is.
- earlySuccessCriteria · enable=true, healthyPercent=90
- 1ª task saudável / first healthy task · pré-condição da avaliação
- Gate: 90 de 100 tasks · arredondado p/ cima / rounded up
- Deployment circuit breaker · BOUNDED_PERCENT 50 → 3..200
- CloudWatch alarm · + bakeTimeInMinutes
- BLOCKING · drena antes do SUCCESSFUL
- DEFERRED · SUCCESSFUL antes de drenar
- Service scaling · 10 tasks restantes / remaining
- Tasks da revisão fonte · scale-in protection ≤ 2880 min
- DescribeServiceDeployments · snapshot pós-sucesso
- DescribeServices · contagem viva / live counts
- EventBridge · IN_PROGRESS → SUCCESSFUL
What changes for whoever designs the service
ROLLING only: the BLUE_GREEN, LINEAR and CANARY strategies have no earlySuccessCriteria — they already solve the same problem through traffic shifting, at a different cost.minimumHealthyPercent closes the range: healthyPercent is an integer between 0 and 100 and must sit between the service minimumHealthyPercent and 100. With the 100% default for replica services, there is no range left.The line you are moving is the rollback line
Reading this as a faster deploy is reading it wrong. healthyPercent is not an accelerator — it is a declaration of where automatic protection ends. The documentation is explicit: after ECS completes the deployment, the circuit breaker and alarm-based rollback no longer roll the service back, including while the remaining tasks launch through regular service scaling.
Translated to on-call terms: with a desired count of 100 and healthyPercent 90, the last ten tasks launch without a net. If the ninety-fifth fails for a reason that only shows under real load — a connection pool running out, a secret that only resolves in one AZ, a per-instance license cap — ECS replaces it with the same target revision, repeatedly, without returning to the previous one. The failure mode is not the deployment failing; it is the deployment passing and the service degrading slowly, with the pipeline already green and the team already on another task.
The circuit breaker still protects what is left of the window, and its sensitivity matters. The default BOUNDED_PERCENT type multiplies the value (default 50) by the desired count and clamps the result between 3 and 200: with 100 desired tasks that is 50 tolerated failures before rollback; with 400, the 200 cap applies. Worse, with resetOnHealthyTask at true — the default — the counter resets on every healthy task, so interleaved failures never accumulate. Shortening the protected window with healthyPercent while leaving the circuit breaker on its permissive default is applying two relaxations at once and calling it speed.
BLOCKING or DEFERRED: same flag, two different risks
| BLOCKING | DEFERRED | |
|---|---|---|
| When it turns SUCCESSFUL | After healthyPercent, the bake time and the cleanup of source revision tasks. | After healthyPercent and the bake time. Cleanup happens outside the deployment. |
| Source revision tasks | Already gone when the pipeline gets its green light. | May keep running; ECS tries to drain them for up to two weeks. |
| Capacity cost | Predictable: both revisions coexist only inside the deployment window. | Two revisions billing at once for an indeterminate period, within maximumPercent. |
| Risk you accept | A longer deployment if draining has a long tail; CI/CD tool timeouts. | Traffic served by old code after the deploy is declared successful. |
| Use when | This is your default. Pick it unless you have a written reason not to. | Long-lived connections, game sessions, async jobs, or task scale-in protection with short expiry. |
Choosing the number with arithmetic, not faith
Three constraints close the range before any opinion enters. First: healthyPercent must be greater than or equal to the service minimumHealthyPercent. If you kept the 100% replica-service default, there is no range at all — lowering minimumHealthyPercent is a prerequisite, and it governs how many tasks the scheduler may stop during the swap, which is a capacity decision, not a speed one.
Second: the value is rounded up against the desired count. With 3 tasks at 50%, the deployment completes at 2. With 10 at 80%, at 8. On a small service each percentage point is worth a whole task, and the time saved trends to zero: earlySuccessCriteria only pays on a large fleet or on slow capacity.
Third: ECS launches at least one task on the target revision and waits for it to become healthy before evaluating the percentage. And a task only counts as healthy after passing the health checks you configured — with a load balancer that means the target group health check, and with an essential container that defines a health check, both.
From there it is a risk decision, and a conditional one. Use 90% or more when the service is stateless behind an ALB and the health check exercises a real dependency, not a /healthz that returns 200 without touching anything. Stay at 100% — or leave the feature off — when the tail of tasks carries state, a shard or a per-instance license. And if the motivation is a slow pipeline, measure first: in a rolling deployment the time usually lives in healthCheckGracePeriodSeconds, in the container health check startPeriod and in the target group deregistration delay — none of which the success rule changes.
DEFERRED has an expiry date
In DEFERRED mode, ECS tries to clean up source revision tasks for up to two weeks after success — and it cannot stop tasks with active scale-in protection. Protection caps at 2880 minutes (48 hours) per call, but it is renewable: a worker that renews its own protection in a loop keeps the old revision alive past the window and ECS gives up. Add the 3-hour CloudFormation update-stack timeout and you have two revisions billing at once, serving traffic, with nobody watching — because the deployment that created them is already marked successful. Instrument the age of source revision tasks via DescribeServices; there is no ready-made alarm for it.
What the dashboard starts saying — and what it stops saying
The feature invents no new status. The lifecycle stays IN_PROGRESS → SUCCESSFUL, the EventBridge service deployment state change events and CloudTrail records are the same, and a deployment that completed early shows up normally when you filter ListServiceDeployments by SUCCESSFUL. That is excellent for compatibility and terrible for auditing: no consumer of your event bus can tell rolled out fully apart from rolled out to 90% and handed the rest to autoscaling. If your change process needs that distinction — and under BACEN, PCI-DSS or SOX it does — the source is DescribeServiceDeployments, which returns the early success criteria configuration alongside the deployment.
There is a silent trap in the counters. While the deployment is in progress, DescribeServiceDeployments returns live counts; after it completes, it returns a snapshot from the moment of completion, in both cleanup modes. A dashboard reading task count from there will show 90 forever, even with 100 tasks running and even with the source revision still draining. The live count lives in DescribeServices, and nowhere else.
So the alarm that matters moves. Before, an EventBridge rule with eventName equal to SERVICE_DEPLOYMENT_FAILED covered most of the release risk; now it covers only the window up to healthyPercent. What deserves an alarm instead is the gap between desiredCount and runningCount on the target revision a few minutes after success, the number of task replacements per revision, and the age of source revision tasks in DEFERRED mode. None of those three comes for free.
How this will be used wrong
healthyPercentas a pipeline accelerator: dropping to 50% because the build is slow buys pipeline minutes with half the fleet launching without automatic rollback. The time was in the health check and draining timers, not in the success rule.- One value for every service via a shared Terraform module: the right number depends on desired count, capacity cost and whether the service carries state. A global
healthyPercentis the definition of a setting nobody reviews. DEFERREDcombined with scale-in protection renewed in a loop: old tasks become permanent, ECS gives up after two weeks, and you find out from the invoice rather than the dashboard.- Keeping the release dashboard on
DescribeServiceDeployments: after success the counters become a snapshot. The panel stays green and frozen while the tail of the fleet does whatever it wants.
What actually changed
What changed is not the duration of the deployment — it is who answers for the tail. With earlySuccessCriteria, the launch of the last tasks moves from the deployment plane, which has rollback, a circuit breaker and a failure event, to the service scaling plane, which has none of the three. It is the same choice you already make when you set an SLO: accepting known error in exchange for speed. The difference is that here the choice becomes an integer in an infrastructure-as-code file, and an integer in an IaC file does not get a risk review — it gets a syntax review.
Today I would enable this on exactly one kind of service: inference on specialized capacity, where the tail of tasks depends on hardware availability rather than on my code — and even then with healthyPercent at 90, sourceServiceRevisionCleanup at BLOCKING, and the circuit breaker switched to a COUNT thresholdConfiguration with a low value, precisely because the protected window got shorter. On a payments service I would not enable it: the gain is pipeline minutes and the loss is automatic rollback under real load, which is exactly the control I cannot rebuild at 2 AM. The lesson that cost me dearly on financial platforms: every mechanism that unblocks the pipeline sends its bill on the first failure that only appears with production traffic, and the bill arrives on-call, not in the retro. Before touching the success criterion I measure where the deployment actually spends time — when the answer is grace period and deregistration delay, healthyPercent would have changed nothing and the risk would have been taken for free.
References
Verdict
Enable earlySuccessCriteria when the tail of your deployment depends on capacity you do not control — GPU, specialized instances, long-lived connection draining — and when the service is stateless behind a health check that exercises a real dependency. In that case: healthyPercent between 90 and 95, BLOCKING as the default, DEFERRED only where long-lived connections or short, monitored scale-in protection exist, the circuit breaker switched to COUNT to compensate for the shorter protected window, and a new alarm on the gap between desiredCount and runningCount a few minutes after success. Do not enable it as a cure for a slow pipeline: rolling deployment time lives in the health check and draining timers, and swapping the success rule only moves risk past the rollback line. The enable: false default remains right for most services — this is a well-designed exception feature, with its boundary documented out loud, not a new baseline.
Architecture, AWS, AI and market deep dives — straight to your inbox. Free.
No spam · unsubscribe anytime
Ask Fernando about this
Get a focused answer about this article from my AI assistant, grounded in my work.
Join the conversation
Sign in to comment
Verify your email to join in — you'll also get the newsletter. No password.
Keep reading
Architecture intelligence, in your inbox
Curated signals and original analysis on AWS, AI, distributed systems and the market — the way a solutions architect reads them.
- Curated AWS · AI · architecture · market signals
- New architecture studies & deep-dives when they ship
- Sharp summaries — depth without the noise
- No spam · double opt-in · unsubscribe anytime