I checked the deployment state transition logs with Amazon ECS's new feature "Action Logs"
This page has been translated by machine translation. View original
Introduction
On July 21, 2026, Action Logs became available in Amazon ECS. Orchestration operations performed by ECS during deployments are now automatically recorded as structured logs in CloudWatch Logs.
When Action Logs are enabled, the way deployment status is tracked changes as follows:
| Aspect | Before | After enabling Action Logs |
|---|---|---|
| Deployment status tracking | Check service events (describe-services) each time |
Automatically recorded as structured logs in CloudWatch Logs |
| Identifying image digests | Difficult to identify with :latest tag |
Automatically recorded with SERVICE_REVISION_STABLE |
| Detecting circuit breaker activation | Check via service events | Recorded as WARN level log, enabling alert integration |
| Isolating failure causes | Manually correlate service events and task status | Classifiable by event pattern (presence of SERVICE_REVISION_STABLE) |
| Log search and analysis | Visual inspection of unstructured text | Queryable with CloudWatch Logs Insights |
In this article, we enabled Action Logs via CLI and observed the events output in three scenarios: successful deployment, OOM failure, and image pull failure. The summary of results for the three scenarios is as follows:
| Pattern | SERVICE_REVISION_STABLE | Rollback observed | Result |
|---|---|---|---|
| Successful deployment | Present (digest recorded) | — | SERVICE_DEPLOYMENT_SUCCESSFUL |
| OOM failure | Present (digest recorded) | Yes (approx. 4 minutes) | SERVICE_DEPLOYMENT_ROLLBACK_SUCCESSFUL |
| Image pull failure | Absent | No (not reached after 18+ minutes) | SERVICE_DEPLOYMENT_IN_PROGRESS only |
Verification Details
Verification Environment
Verification was performed targeting 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 use the CloudWatch Logs Vended Logs delivery mechanism. It is enabled in 5 steps: create log group → resource policy → delivery source → delivery destination → 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 (grant write permission 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
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
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
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 verification had the following structure (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 |
Event source (in service/<cluster-name>/<service-name> format; also used as the log stream name) |
logLevel |
Log level (INFO / WARN / ERROR) |
eventTimestamp |
Event occurrence time (epoch milliseconds) |
detail.eventName |
Event type |
detail.statusReason |
Human-readable status reason |
Log streams are created with the naming convention service/<cluster-name>/<service-name>, making it easy to filter by service.
The eventName values confirmed in this verification 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 activated, rollback in progress |
| SERVICE_DEPLOYMENT_ROLLBACK_SUCCESSFUL | INFO | Rollback completed |
Verifying Logs for Successful Deployment
A service was created with the nginx image (action-logs-test-normal:1) and the events output during a successful deployment were verified.
| Elapsed | eventName | logLevel |
|---|---|---|
| 0 sec | SERVICE_DEPLOYMENT_IN_PROGRESS | INFO |
| +34 sec | SERVICE_REVISION_STABLE | INFO |
| +approx. 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"
}
}
Worth noting is the SERVICE_REVISION_STABLE event. The statusReason records Locked to container image(s): ...@sha256:709204... with the image digest included. Even when deploying with the :latest tag, you can later identify the digest of the image that was actually running.
Verifying Logs for Deployment Failure (OOM → Circuit Breaker)
Next, we verified the logs when OOM is intentionally triggered and the circuit breaker activates. The alpine image container memory limit was restricted to 6 MiB, and memory was consumed using dd if=/dev/zero, resulting in an OOM immediately after startup.
| Elapsed | eventName | logLevel |
|---|---|---|
| 0 sec | SERVICE_DEPLOYMENT_IN_PROGRESS | INFO |
| +53 sec | SERVICE_REVISION_STABLE | INFO |
| +approx. 3 min 45 sec | SERVICE_DEPLOYMENT_ROLLBACK | WARN |
| +approx. 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, since the image itself could be resolved, SERVICE_REVISION_STABLE was output. After that, as the task continued to fail to start due to OOM, SERVICE_DEPLOYMENT_ROLLBACK was recorded at WARN level. Since the statusReason explicitly states the reason for exceeding the threshold, it can be used to detect rollbacks in conjunction with CloudWatch Alarms.
Verifying Logs for Image Pull Failure
Finally, the service was updated to use the non-existent image public.ecr.aws/does-not-exist/fake-image:v9999.
Only the event indicating the start of the deployment was output to Action Logs (within the range observable during the verification 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 the digest could not be locked. In other words, the presence or absence of SERVICE_REVISION_STABLE allows you to distinguish whether the failure occurred before or after image resolution.
The traditional service events recorded per-container failure reasons.
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 record at the deployment-wide state transition level, they can be used in a complementary manner with traditional service events that contain per-container details.
Note that the time for the circuit breaker to activate was approximately 4 minutes in the OOM case. In the image pull failure case, the circuit breaker had not activated even after more than 18 minutes had elapsed, so the SERVICE_DEPLOYMENT_ROLLBACK log was not obtained.
CloudWatch Logs Insights Query Examples
Here are some query examples useful for operations.
-- Extract WARN/ERROR events (for deployment failure investigation)
fields @timestamp, detail.eventName, logLevel, detail.statusReason
| filter logLevel in ["WARN", "ERROR"]
| sort @timestamp asc
| limit 50
-- All events in chronological order (service level)
fields @timestamp, detail.eventName, logLevel, detail.status, detail.statusReason
| filter @logStream like /^service\//
| sort @timestamp asc
| limit 50
-- Tracking a specific deployment
fields @timestamp, detail.eventName, logLevel, detail.statusReason
| filter detail.serviceDeploymentArn like /<deployment-id>/
| sort @timestamp asc
-- Detecting 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, we were able to track deployment progress, completion, and rollbacks on CloudWatch Logs. In this verification, combining the presence or absence of SERVICE_REVISION_STABLE output with 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.
