# ECS Early Success Criteria: where your deployment rollback ends

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.

- URL: https://fernando.moretes.com/blog/ecs-early-success-criteria-onde-termina-o-rollback-do-seu-deploy-amazon-ecs-i

- Markdown: https://fernando.moretes.com/blog/ecs-early-success-criteria-onde-termina-o-rollback-do-seu-deploy-amazon-ecs-i/article.md?lang=en

- Published: 2026-09-07T10:16:02.363Z

- Category: AI & Agents

- Tags: ecs, deployments, ci-cd, reliability, containers, sre, aws

- Reading time: 8 min

- Source: [Amazon ECS introduces Early Success Criteria for service deployments](https://aws.amazon.com/about-aws/whats-new/2026/09/amazon-ecs-deployments-early-success/)

---

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

- **90/100** — healthy tasks to complete the deployment. With `healthyPercent` 90 and a desired count of 100. The remaining ten launch through regular service scaling, outside the deployment lifecycle.
- **2 semanas / 2 we** — cleanup window in DEFERRED mode. ECS tries to drain source revision tasks for up to two weeks after success. A task protected beyond that is never cleaned up.
- **0** — new deployment statuses. The lifecycle stays `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.

### 🟧 AWS ECS — janela protegida (rollback ativo)

- earlySuccessCriteria enable=true, healthyPercent=90 (compute)
- 1ª task saudável / first healthy task pré-condição da avaliação (compute)
- Gate: 90 de 100 tasks arredondado p/ cima / rounded up (compute)
- Deployment circuit breaker BOUNDED_PERCENT 50 → 3..200 (security)
- CloudWatch alarm + bakeTimeInMinutes (security)

### 🔀 Cleanup da revisão fonte / source revision cleanup

- BLOCKING drena antes do SUCCESSFUL (messaging)
- DEFERRED SUCCESSFUL antes de drenar (messaging)

### 🟩 Fora do deployment — sem rollback / no rollback

- Service scaling 10 tasks restantes / remaining (compute)
- Tasks da revisão fonte scale-in protection ≤ 2880 min (compute)

### 📡 Sinais / observability

- DescribeServiceDeployments snapshot pós-sucesso (data)
- DescribeServices contagem viva / live counts (data)
- EventBridge IN_PROGRESS → SUCCESSFUL (messaging)

### Flows

- cicd -> cfg: applies deployment-configuration
- cfg -> first: ECS waits for 1 healthy task
- first -> gate: counts healthy tasks
- cb -> gate: can roll back up to here
- alarm -> gate: bake time before completion
- gate -> blocking: cleanup = BLOCKING
- gate -> deferred: cleanup = DEFERRED
- blocking -> scaling: success after draining
- deferred -> scaling: success before draining
- deferred -> drain: async drain, up to 2 weeks
- scaling -> ds: only source of real counts
- gate -> dsd: freezes the counters
- gate -> eb: same event as always
- cicd -> eb: unblocks the next deploy

## 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.
- **It rounds up:** a desired count of 3 at 50% completes at 2 tasks, not 1. A desired count of 10 at 80% completes at 8. On a small service the gain is zero.
- **There is no shortcut to zero:** ECS launches at least one task on the target revision and waits for it to become healthy before evaluating the percentage. Broken code does not complete early — it fails.
- **Once complete, it is final:** the circuit breaker and alarm-based rollback stop acting after success, including while the remaining tasks launch. A completed deployment cannot be stopped.

## 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
| Criterion | 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

- **`healthyPercent` as 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 `healthyPercent` is the definition of a setting nobody reviews.
- **`DEFERRED` combined 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.

> **Curation note:** 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

- [Amazon ECS introduces Early Success Criteria for service deployments (AWS What's New, 04/09/2026)](https://aws.amazon.com/about-aws/whats-new/2026/09/amazon-ecs-deployments-early-success/)
- [Complete Amazon ECS rolling deployments early with early success criteria](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/early-success-criteria.html)
- [DeploymentEarlySuccessCriteria — Amazon ECS API Reference](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DeploymentEarlySuccessCriteria.html)
- [DeploymentConfiguration — Amazon ECS API Reference](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DeploymentConfiguration.html)
- [How the Amazon ECS deployment circuit breaker detects failures](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-circuit-breaker.html)
- [Protect your Amazon ECS tasks from being terminated by scale-in events](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-scale-in-protection.html)
- [Deploy Amazon ECS services by replacing tasks (rolling update)](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-type-ecs.html)
- [What Actually Happens During an ECS Rolling Deployment — Muhammad Raza](https://muhammadraza.me/2026/what-actually-happens-during-an-ecs-rolling-deployment/)

## 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.

**Rating:** Ligar por exceção, não por padrão / Enab
