Trying Out Amazon ECS's New "Action Logs" Feature to Check Deployment State Transition Logs

Trying Out Amazon ECS's New "Action Logs" Feature to Check Deployment State Transition Logs

Action Logs are now available in Amazon ECS, recording the operations ECS performs during deployments as structured logs in CloudWatch Logs. We checked the events for successful, OOM-failed and image-pull-failed deployments.
2026.07.22

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.

https://aws.amazon.com/about-aws/whats-new/2026/07/amazon-ecs-action-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

https://docs.aws.amazon.com/AmazonECS/latest/developerguide/action-logs.html

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.

References

Share this article

AWSのお困り事はクラスメソッドへ