Trying Out Amazon ECS's New "Action Logs" Feature to Check Deployment State Transition Logs
This page has been translated by machine translation. View original
Introduction
On July 21, 2026, Action Logs became available in Amazon ECS. The orchestration operations that ECS performs during a deployment are now automatically recorded as structured logs in CloudWatch Logs.
Enabling Action Logs changes how you track deployment status, as follows.
| Aspect | Before | After enabling Action Logs |
|---|---|---|
| Tracking deployment status | Check service events (describe-services) each time |
Automatically recorded in CloudWatch Logs as structured logs |
| Identifying image digests | Difficult to identify when using the :latest tag |
Automatically recorded in SERVICE_REVISION_STABLE |
| Detecting circuit breaker activation | Check in service events | Recorded as a WARN-level log, enabling alert integration |
| Isolating the cause of failures | Manually correlate service events and task statuses | Can be classified by event pattern (presence or absence of SERVICE_REVISION_STABLE) |
| Log search and analysis | Visually inspect unstructured text | Queryable with CloudWatch Logs Insights |
In this article, I enabled Action Logs via the CLI and checked the events output in three patterns: successful deployment, OOM failure, and image pull failure. The summary of the results for the three patterns is as follows.
| Pattern | SERVICE_REVISION_STABLE | Rollback observed | Result |
|---|---|---|---|
| Successful deployment | Yes (digest recorded) | — | SERVICE_DEPLOYMENT_SUCCESSFUL |
| OOM failure | Yes (digest recorded) | Yes (about 4 minutes) | SERVICE_DEPLOYMENT_ROLLBACK_SUCCESSFUL |
| Image pull failure | No | No (not reached after 18+ minutes) | SERVICE_DEPLOYMENT_IN_PROGRESS only |
What I Tested
Test environment
I tested a service on Fargate in the ap-northeast-1 region. The deployment circuit breaker was enabled (rollback=true, threshold 50%).
| Task definition | Purpose | Image | CPU | Memory (task) | Memory (container hard limit) |
|---|---|---|---|---|---|
| action-logs-test-normal:1 | Successful deployment | public.ecr.aws/nginx/nginx:latest | 256 | 512 MiB | — |
| action-logs-test-oom:1 | OOM failure | public.ecr.aws/docker/library/alpine:latest | 256 | 512 MiB | 6 MiB |
| action-logs-test-pull-fail:1 | Image pull failure | public.ecr.aws/does-not-exist/fake-image:v9999 | 256 | 512 MiB | — |
Enabling Action Logs (CLI)
Action Logs uses the Vended Logs delivery mechanism of CloudWatch Logs. You enable it in five steps: create a log group → resource policy → delivery source (Delivery Source) → delivery destination (Delivery Destination) → delivery (Delivery). Specify ACTION_LOGS for the log-type of the delivery source.
Required IAM permissions:
- logs:CreateLogGroup
- logs:PutRetentionPolicy
- logs:PutResourcePolicy
- logs:PutDeliverySource
- logs:PutDeliveryDestination
- logs:CreateDelivery
- logs:GetDelivery
- ecs:AllowVendedLogDeliveryForResource
CLI commands for each step:
# 1. Create log group
aws logs create-log-group \
--log-group-name /aws/vendedlogs/ecs/action-logs/<cluster-name> \
--region ap-northeast-1
aws logs put-retention-policy \
--log-group-name /aws/vendedlogs/ecs/action-logs/<cluster-name> \
--retention-in-days 7 \
--region ap-northeast-1
# 2. Resource policy (allow writes to delivery.logs.amazonaws.com)
aws logs put-resource-policy \
--policy-name ecs-action-logs-delivery-policy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowECSActionLogsDelivery",
"Effect": "Allow",
"Principal": {"Service": "delivery.logs.amazonaws.com"},
"Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/vendedlogs/ecs/action-logs/<cluster-name>:log-stream:*",
"Condition": {
"StringEquals": {"aws:SourceAccount": "123456789012"},
"ArnLike": {"aws:SourceArn": "arn:aws:ecs:ap-northeast-1:123456789012:*"}
}
}]
}' \
--region ap-northeast-1
# 3. Delivery source (Delivery Source)
aws logs put-delivery-source \
--name <source-name> \
--resource-arn arn:aws:ecs:ap-northeast-1:123456789012:cluster/<cluster-name> \
--log-type ACTION_LOGS \
--region ap-northeast-1
# 4. Delivery destination (Delivery Destination)
aws logs put-delivery-destination \
--name <destination-name> \
--output-format json \
--delivery-destination-configuration '{"destinationResourceArn":"arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/vendedlogs/ecs/action-logs/<cluster-name>"}' \
--region ap-northeast-1
# 5. Create delivery (Delivery)
aws logs create-delivery \
--delivery-source-name <source-name> \
--delivery-destination-arn arn:aws:logs:ap-northeast-1:123456789012:delivery-destination:<destination-name> \
--region ap-northeast-1
Action Logs schema
The events obtained in this test had the following structure (the field placement may differ from the schema definition in the official documentation).
{
"resourceArn": "arn:aws:ecs:<region>:<account>:cluster/<cluster-name>",
"actionSourceId": "service/<cluster-name>/<service-name>",
"logLevel": "INFO | WARN | ERROR",
"eventTimestamp": 1784663292399,
"detail": {
"eventName": "<event-name>",
"status": "IN_PROGRESS | SUCCEEDED",
"statusReason": "<human-readable reason>",
"serviceDeploymentArn": "arn:aws:ecs:...:service-deployment/...",
"serviceRevisionArn": "arn:aws:ecs:...:service-revision/..."
}
}
| Field | Meaning |
|---|---|
resourceArn |
ARN of the target cluster |
actionSourceId |
Source of the event (in the format service/<cluster-name>/<service-name>. Also used as the log stream name) |
logLevel |
Log level (INFO / WARN / ERROR) |
eventTimestamp |
Time the event occurred (epoch milliseconds) |
detail.eventName |
Event type |
detail.statusReason |
Human-readable reason for the status |
Log streams are created with the naming convention service/<cluster-name>/<service-name>, which makes filtering by service easy.
The eventName values confirmed in this test are as follows.
| eventName | logLevel | Meaning |
|---|---|---|
| SERVICE_DEPLOYMENT_IN_PROGRESS | INFO | Deployment started |
| SERVICE_REVISION_STABLE | INFO | Service revision stable, locked to image digest |
| SERVICE_DEPLOYMENT_SUCCESSFUL | INFO | Deployment completed successfully |
| SERVICE_DEPLOYMENT_ROLLBACK | WARN | Circuit breaker triggered, rollback in progress |
| SERVICE_DEPLOYMENT_ROLLBACK_SUCCESSFUL | INFO | Rollback completed |
Checking logs for a successful deployment
I created a service with the nginx image (action-logs-test-normal:1) and checked the events output during a successful deployment.
| Elapsed | eventName | logLevel |
|---|---|---|
| 0 sec | SERVICE_DEPLOYMENT_IN_PROGRESS | INFO |
| +34 sec | SERVICE_REVISION_STABLE | INFO |
| +about 3 min | SERVICE_DEPLOYMENT_SUCCESSFUL | INFO |
Full structure of each event:
{
"resourceArn": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/action-logs-test-cluster",
"actionSourceId": "service/action-logs-test-cluster/action-logs-test-service",
"logLevel": "INFO",
"eventTimestamp": 1784663292399,
"detail": {
"statusReason": "Service deployment in progress.",
"serviceDeploymentArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-deployment/action-logs-test-cluster/action-logs-test-service/xQiDJv98PtZRdF7HNahyb",
"status": "IN_PROGRESS",
"eventName": "SERVICE_DEPLOYMENT_IN_PROGRESS"
}
}
{
"resourceArn": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/action-logs-test-cluster",
"actionSourceId": "service/action-logs-test-cluster/action-logs-test-service",
"logLevel": "INFO",
"eventTimestamp": 1784663326125,
"detail": {
"statusReason": "Service revision marked stable. Locked to container image(s): public.ecr.aws/nginx/nginx:latest@sha256:709204fab25be0425d55090c79f3e84161cf99f50278543d7f6bf372e326ad1f.",
"serviceRevisionArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-revision/action-logs-test-cluster/action-logs-test-service/7911470091089632318",
"status": "SUCCEEDED",
"eventName": "SERVICE_REVISION_STABLE"
}
}
{
"resourceArn": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/action-logs-test-cluster",
"actionSourceId": "service/action-logs-test-cluster/action-logs-test-service",
"logLevel": "INFO",
"eventTimestamp": 1784663469912,
"detail": {
"statusReason": "Service deployment completed successfully.",
"serviceDeploymentArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-deployment/action-logs-test-cluster/action-logs-test-service/xQiDJv98PtZRdF7HNahyb",
"status": "SUCCEEDED",
"eventName": "SERVICE_DEPLOYMENT_SUCCESSFUL"
}
}
The SERVICE_REVISION_STABLE event is the one to note. The statusReason records Locked to container image(s): ...@sha256:709204... with the image digest. Even when you deploy with the :latest tag, you can later identify the digest of the image that was actually running.
Checking logs for a failed deployment (OOM → circuit breaker)
Next, I checked the logs when the circuit breaker is triggered by intentionally causing an OOM. I limited the container memory of the alpine image to 6 MiB and consumed memory with dd if=/dev/zero, so that an OOM occurs immediately after startup.
| Elapsed | eventName | logLevel |
|---|---|---|
| 0 sec | SERVICE_DEPLOYMENT_IN_PROGRESS | INFO |
| +53 sec | SERVICE_REVISION_STABLE | INFO |
| +about 3 min 45 sec | SERVICE_DEPLOYMENT_ROLLBACK | WARN |
| +about 4 min | SERVICE_DEPLOYMENT_ROLLBACK_SUCCESSFUL | INFO |
Full structure of each event:
{
"logLevel": "INFO",
"eventTimestamp": 1784663519017,
"detail": {
"statusReason": "Service deployment in progress.",
"serviceDeploymentArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-deployment/action-logs-test-cluster/action-logs-test-service/Z6HSP7lksB3wTV0bOxmpF",
"status": "IN_PROGRESS",
"eventName": "SERVICE_DEPLOYMENT_IN_PROGRESS"
}
}
{
"logLevel": "INFO",
"eventTimestamp": 1784663571619,
"detail": {
"statusReason": "Service revision marked stable. Locked to container image(s): public.ecr.aws/docker/library/alpine:latest@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b.",
"serviceRevisionArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-revision/action-logs-test-cluster/action-logs-test-service/2005995910462199077",
"status": "SUCCEEDED",
"eventName": "SERVICE_REVISION_STABLE"
}
}
{
"logLevel": "WARN",
"eventTimestamp": 1784663746287,
"detail": {
"statusReason": "Service deployment rolled back because the circuit breaker threshold was exceeded.",
"serviceDeploymentArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-deployment/action-logs-test-cluster/action-logs-test-service/Z6HSP7lksB3wTV0bOxmpF",
"status": "IN_PROGRESS",
"eventName": "SERVICE_DEPLOYMENT_ROLLBACK"
}
}
{
"logLevel": "INFO",
"eventTimestamp": 1784663762604,
"detail": {
"statusReason": "Service deployment rolled back because the circuit breaker threshold was exceeded.",
"serviceDeploymentArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-deployment/action-logs-test-cluster/action-logs-test-service/Z6HSP7lksB3wTV0bOxmpF",
"status": "SUCCEEDED",
"eventName": "SERVICE_DEPLOYMENT_ROLLBACK_SUCCESSFUL"
}
}
In this pattern, the image itself could be resolved, so SERVICE_REVISION_STABLE was output. After that, as tasks continued to fail to start due to OOM, SERVICE_DEPLOYMENT_ROLLBACK was recorded at the WARN level. Since the statusReason explicitly states the reason the threshold was exceeded, it can be used to detect rollbacks in combination with CloudWatch Alarms.
Checking logs for an image pull failure
Finally, I updated the service to a nonexistent image, public.ecr.aws/does-not-exist/fake-image:v9999.
The only event output to Action Logs was the one indicating the start of the deployment (within the range observable during the test period).
{
"logLevel": "INFO",
"eventTimestamp": 1784663869100,
"detail": {
"statusReason": "Service deployment in progress.",
"serviceDeploymentArn": "arn:aws:ecs:ap-northeast-1:123456789012:service-deployment/action-logs-test-cluster/action-logs-test-service/IB_8BjTwukTyDzQ4uW4Ca",
"status": "IN_PROGRESS",
"eventName": "SERVICE_DEPLOYMENT_IN_PROGRESS"
}
}
SERVICE_REVISION_STABLE was not output. This is because the image could not be resolved and locking to a digest could not be performed. In other words, the presence or absence of SERVICE_REVISION_STABLE lets you distinguish whether the failure occurred at the image resolution stage, or whether the image was resolved but the failure occurred at runtime.
The conventional service events recorded the per-container failure reason.
CannotPullContainerError: pull image manifest has been retried 7 time(s):
failed to resolve ref public.ecr.aws/does-not-exist/fake-image:v9999:
public.ecr.aws/does-not-exist/fake-image:v9999: not found.
Since Action Logs are recorded at the level of state transitions for the deployment as a whole, they can be used to complement the conventional service events, which carry per-container details.
As for the time taken until the circuit breaker was triggered, the OOM case took about 4 minutes. In the image pull failure case, the circuit breaker was not triggered even after more than 18 minutes, so I could not obtain a SERVICE_DEPLOYMENT_ROLLBACK log.
CloudWatch Logs Insights query examples
Here are some query examples that can be used in operations.
-- Extract WARN/ERROR events (for investigating deployment failures)
fields @timestamp, detail.eventName, logLevel, detail.statusReason
| filter logLevel in ["WARN", "ERROR"]
| sort @timestamp asc
| limit 50
-- Timeline of all events (service level)
fields @timestamp, detail.eventName, logLevel, detail.status, detail.statusReason
| filter @logStream like /^service\//
| sort @timestamp asc
| limit 50
-- Track a specific deployment
fields @timestamp, detail.eventName, logLevel, detail.statusReason
| filter detail.serviceDeploymentArn like /<deployment-id>/
| sort @timestamp asc
-- Detect circuit breaker activation
fields @timestamp, detail.eventName, detail.statusReason
| filter detail.eventName = "SERVICE_DEPLOYMENT_ROLLBACK"
| sort @timestamp desc
| limit 20
Summary
With ECS Action Logs, I was able to track the progress, completion, and rollback of deployments in CloudWatch Logs. In this test, combining the presence or absence of SERVICE_REVISION_STABLE with the subsequent events made it easier to investigate failures before and after image resolution. The rollback start event can also be used as an alert condition for detecting deployment failures.
