I Tried Configuring and Checking the Health Check Logs Now Supported by ALB Using CloudFormation
This page has been translated by machine translation. View original
On November 21, 2025, an upgrade was released that makes "health check logs" available for the Application Load Balancer (ALB).
Have you ever struggled to investigate the cause when an ALB target became "Unhealthy"? Even when trying to check the application logs on the target side, it was often difficult to identify the cause because requests were not reaching the target, or the instance had already been terminated by Auto Scaling.
This time, I had the opportunity to build a test environment using CloudFormation and check the health check logs actually output to S3, so I'd like to share it with you.
Test Environment
I created an ALB with health check logs enabled. I prepared two EC2 instances as targets, and configured one of them to intentionally fail the health check (web server not running).
Architecture Diagram

S3 Bucket Policy
I used a bucket policy to allow ALB to write logs.
LogsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref LogsBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSLogDeliveryWrite
Effect: Allow
Principal:
Service: logdelivery.elasticloadbalancing.amazonaws.com
Action: s3:PutObject
Resource:
# Allow path for access logs
- !Sub '${LogsBucket.Arn}/access-logs/AWSLogs/${AWS::AccountId}/*'
# Allow path for health check logs
- !Sub '${LogsBucket.Arn}/health-check-logs/AWSLogs/${AWS::AccountId}/*'
ALB
In LoadBalancerAttributes, I added the health check log setting (health_check_logs), which is the new feature this time.
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
# --- Health Check Logs Settings ---
- Key: health_check_logs.s3.enabled
Value: 'true'
- Key: health_check_logs.s3.bucket
Value: !Ref LogsBucket
- Key: health_check_logs.s3.prefix
Value: 'health-check-logs'
CloudFormation
Template for creating the test environment
AWSTemplateFormatVersion: '2010-09-09'
Description: 'ALB Access & Health Check Logs (Separated Prefixes) Test Environment'
Parameters:
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64
VpcId:
Type: AWS::EC2::VPC::Id
Description: Default VPC ID
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: At least 2 public subnets in different AZs
Resources:
# S3 Bucket for Logs
LogsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'alb-logs-${AWS::AccountId}-${AWS::Region}'
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
# S3 Bucket Policy
LogsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref LogsBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSLogDeliveryWrite
Effect: Allow
Principal:
Service: logdelivery.elasticloadbalancing.amazonaws.com
Action: s3:PutObject
Resource:
# アクセスログ用のパス許可
- !Sub '${LogsBucket.Arn}/access-logs/AWSLogs/${AWS::AccountId}/*'
# ヘルスチェックログ用のパス許可
- !Sub '${LogsBucket.Arn}/health-check-logs/AWSLogs/${AWS::AccountId}/*'
# Security Group for ALB
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for ALB
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
# Security Group for EC2
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for EC2 instances
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
# IAM Role for EC2
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
# EC2 Instance 1 (Healthy)
EC2Instance1:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref LatestAmiId
InstanceType: t4g.nano
IamInstanceProfile: !Ref EC2InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: '0'
GroupSet:
- !Ref EC2SecurityGroup
SubnetId: !Select [0, !Ref SubnetIds]
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Healthy Instance 1 (ARM)</h1>" > /var/www/html/index.html
Tags:
- Key: Name
Value: ALB-Target-Healthy
# EC2 Instance 2 (Unhealthy)
EC2Instance2:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref LatestAmiId
InstanceType: t4g.nano
IamInstanceProfile: !Ref EC2InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: '0'
GroupSet:
- !Ref EC2SecurityGroup
SubnetId: !Select [1, !Ref SubnetIds]
Tags:
- Key: Name
Value: ALB-Target-Unhealthy
# Target Group
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-logs-test-tg
Port: 80
Protocol: HTTP
VpcId: !Ref VpcId
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckProtocol: HTTP
Targets:
- Id: !Ref EC2Instance1
- Id: !Ref EC2Instance2
# Application Load Balancer
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
DependsOn: LogsBucketPolicy
Properties:
Name: alb-logs-test
Type: application
Scheme: internet-facing
SecurityGroups:
- !Ref ALBSecurityGroup
Subnets: !Ref SubnetIds
LoadBalancerAttributes:
# --- Access Logs Settings ---
- Key: access_logs.s3.enabled
Value: 'true'
- Key: access_logs.s3.bucket
Value: !Ref LogsBucket
- Key: access_logs.s3.prefix
Value: 'access-logs' # フォルダ分け: access-logs/
# --- Health Check Logs Settings ---
- Key: health_check_logs.s3.enabled
Value: 'true'
- Key: health_check_logs.s3.bucket
Value: !Ref LogsBucket
- Key: health_check_logs.s3.prefix
Value: 'health-check-logs' # フォルダ分け: health-check-logs/
# Listener
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
Outputs:
ALBDNSName:
Description: ALB DNS Name
Value: !GetAtt ApplicationLoadBalancer.DNSName
S3BucketName:
Description: S3 Bucket for Logs
Value: !Ref LogsBucket
Verification
After deployment, I confirmed connectivity to the ALB.
# Set the ALB DNS name to a variable
ALB_DNS="<alb-name>-<random-id>.<region>.elb.amazonaws.com"
# Connectivity check
curl http://${ALB_DNS}
Checking the Health Check Log Output
After waiting a few minutes, I checked the S3 output destination of the health check logs.
Logs had been generated under the health-check-logs prefix.
BUCKET_NAME="alb-logs-<account-id>-<region>"
aws s3 ls s3://${BUCKET_NAME}/health-check-logs/
Hierarchy overview
s3://alb-logs-123456789012-ap-northeast-1/
├── access-logs/AWSLogs/123456789012/elasticloadbalancing/ap-northeast-1/2025/11/22/
└── health-check-logs/AWSLogs/123456789012/elasticloadbalancing/ap-northeast-1/2025/11/22/
※Both the health check logs and the conventional access logs are output to the same S3 bucket with different prefixes.
I was able to confirm that the health check logs were output in the file name format health_check_log_<account-id>_elasticloadbalancing_<region>_<load-balancer-name>.<load-balancer-id>_<timestamp>_<ip-address>_<random-string>.log.gz.
2025-11-22 15:35:08 362 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1535Z_<ip-1>_<random>.log.gz
2025-11-22 15:35:08 358 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1535Z_<ip-2>_<random>.log.gz
2025-11-22 15:40:08 357 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1540Z_<ip-1>_<random>.log.gz
2025-11-22 15:40:08 357 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1540Z_<ip-2>_<random>.log.gz
I downloaded the log file and checked its contents.
# Download and decompress the log
aws s3 cp s3://<bucket-name>/path/to/your/log.gz .
gzip -d log.gz
cat log
The health check results (success and failure) for the two EC2 instances were recorded.
> download: s3://<bucket-name>/health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/YYYY/MM/DD/health_check_log_<account-id>_elasticloadbalancing_<region>_<alb-name>.<alb-id>_<timestamp>_<ip>_<random>.log.gz to /tmp/hc.log.gz
http 2025-11-22T15:35:10.504741Z 0.001469625 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:35:10.504211Z 0.002958522 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:35:40.535012Z 0.001567201 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:35:40.534548Z 0.002960985 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:36:10.547468Z 0.00154518 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:36:10.547032Z 0.002790372 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:36:40.578086Z 0.001601312 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:36:40.577634Z 0.002930846 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:37:10.608677Z 0.001456432 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:37:10.608229Z 0.00288121 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:37:40.635578Z 0.001517185 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:37:40.635025Z 0.003032974 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:38:10.666143Z 0.001580969 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:38:10.665657Z 0.003204206 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:38:40.695716Z 0.001406524 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:38:40.695266Z 0.00285128 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:39:10.716992Z 0.001626345 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:39:10.716548Z 0.002873815 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:39:40.739579Z 0.001600648 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:39:40.739106Z 0.00284948 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
Log format
| Field | Value | Description |
|---|---|---|
| Protocol | http | Health check protocol |
| Timestamp | 2025-11-22T... | Execution date and time |
| Processing time | 0.001469625 | Health check response time |
| Target | 172.31.x.x:80 | Target IP address and port |
| Target group | alb-logs-test-tg | Name of the target group it belongs to |
| Status | PASS | Health check result (PASS/FAIL) |
| HTTP code | 200 | Response code from the target |
| Reason | - | Failure reason (hyphen on success) |
Summary
Until now, when an ALB health check failed and a target became "Unhealthy", the investigation depended on application logs and access logs on the target side. However, when logs could not be recorded due to resource exhaustion, or the instance itself was replaced by Auto Scaling or the like, it was sometimes difficult to obtain any clues.
With the newly supported health check logs, it is now possible to trace the "root cause of failure" in detail as seen from the ALB side. In this test as well, a TargetError (502) caused by the web server being stopped was clearly recorded, and I could immediately determine that the problem was not in the network path but inside the instance (or the application response).
In environments where there are concerns about network connectivity to targets, or for critical workloads where downtime is unacceptable and a detailed investigation to prevent recurrence is required, these health check logs may be useful. Please give them a try.
