Skip to main content
emnode / learn
Compliance

Stream Elastic Beanstalk logs to CloudWatch

Security Hub ElasticBeanstalk.3 — when a Beanstalk environment scales in or replaces an instance, its logs vanish with it; streaming to CloudWatch keeps the evidence even after the box is gone.

11 min·10 sections·AWS

Last reviewed

Remediates AWS Security Hub: ElasticBeanstalk.3

Streaming Beanstalk logs: the basics

Why logs on a Beanstalk instance are written in disappearing ink

Elastic Beanstalk runs your application on EC2 instances it provisions, scales, and replaces for you. That convenience has a sharp edge for logging: eb-activity.log, the nginx or Apache access logs, and your application's own logs all live on the instance's local disk. When Beanstalk scales in during a quiet hour, swaps an unhealthy instance, or rolls a platform update, that instance is terminated — and every log file on it is destroyed along with the EBS volume. The evidence of what happened just before the failure goes with the box.

ElasticBeanstalk.3 checks whether the environment is configured to stream those instance logs to CloudWatch Logs. It fails if streaming is off. The control is rated High because PCI DSS 10.4.2 and most logging-control frameworks require that audit trails survive the system that produced them — and an ephemeral instance is exactly the system that won't survive. Without streaming, your retained record of a security or availability event is whatever happened to still be running when you went looking.

It's flagged because the default is off, and because the failure mode is silent. Everything works fine until the day you actually need the logs — an incident, a customer dispute, an auditor's sample request — and you discover the instance that held them was recycled three weeks ago. There's no error, no alarm; there's just an empty /var/log on a machine that no longer exists.

In this lesson you'll learn why Elastic Beanstalk's ephemeral instances make local log files unreliable, exactly what ElasticBeanstalk.3 checks, and how to turn on CloudWatch log streaming through the aws:elasticbeanstalk:cloudwatch:logs option namespace. You'll see the AWS CLI commands to inspect an environment's current logging settings and apply the fix, the role of the StreamLogs, DeleteOnTerminate, and RetentionInDays options, the IAM permissions the instance profile needs, and how to bake the setting into .ebextensions so new environments are compliant by default.

Fun fact

The logs that scaled themselves into oblivion

A payments startup chasing PCI certification got hit with a finding mid-assessment: the auditor asked for the access logs covering a specific 48-hour window six weeks earlier. The team confidently opened the Beanstalk environment — and found nothing. An auto-scaling event had terminated the three instances that served traffic that day, and with StreamLogs switched off, the only copies of those logs went to the bit bucket along with the EBS volumes. The remediation took one CLI command and cost about $3 a month in CloudWatch storage. The delay it caused to their certification cost a great deal more than that.

Turning on log streaming in action

Dev is the platform engineer on call when Security Hub raises ElasticBeanstalk.3 against the payments-prod environment. The finding is High, tied to PCI DSS 10.4.2, and the audit window is three weeks out — so this is a fix-today item, not a backlog ticket.

He first inspects the environment's current settings to confirm streaming really is off, reading the aws:elasticbeanstalk:cloudwatch:logs option namespace. StreamLogs comes back false. He checks the instance profile too — the EC2 role attached to the environment needs logs:CreateLogGroup, logs:CreateLogStream, and logs:PutLogEvents, which the managed AWSElasticBeanstalkWebTier policy already grants, so no IAM change is needed.

He applies the fix with a single update-environment call, setting StreamLogs=true, a 90-day RetentionInDays to match the audit retention requirement, and DeleteOnTerminate=false so the CloudWatch log group survives even when the environment is later torn down. Beanstalk performs a rolling configuration update; within a few minutes the /aws/elasticbeanstalk/payments-prod/var/log/... log groups appear in CloudWatch and the next Security Hub evaluation flips the control to PASSED. Then he opens a PR adding the same setting to the team's shared .ebextensions so every future environment is born compliant.

First, inspect the environment's current logging configuration to confirm streaming is off before you change anything.

$ aws elasticbeanstalk describe-configuration-settings --application-name payments --environment-name payments-prod --query 'ConfigurationSettings[].OptionSettings[?Namespace==`aws:elasticbeanstalk:cloudwatch:logs`]' --output table
------------------------------------------------------------------------
| DescribeConfigurationSettings |
+----------------------------------------+------------------+----------+
| Namespace | OptionName | Value |
+----------------------------------------+------------------+----------+
| aws:elasticbeanstalk:cloudwatch:logs | StreamLogs | false |
| aws:elasticbeanstalk:cloudwatch:logs | DeleteOnTerminate| true |
| aws:elasticbeanstalk:cloudwatch:logs | RetentionInDays | 7 |
+----------------------------------------+------------------+----------+
# StreamLogs=false means logs die with the instance — this is the finding.

Read the cloudwatch:logs namespace first; StreamLogs=false is exactly what ElasticBeanstalk.3 fails on.

Turn streaming on, set a retention window that matches your audit requirement, and keep the log group after teardown.

$ aws elasticbeanstalk update-environment --environment-name payments-prod --option-settings Namespace=aws:elasticbeanstalk:cloudwatch:logs,OptionName=StreamLogs,Value=true Namespace=aws:elasticbeanstalk:cloudwatch:logs,OptionName=DeleteOnTerminate,Value=false Namespace=aws:elasticbeanstalk:cloudwatch:logs,OptionName=RetentionInDays,Value=90
{
"EnvironmentName": "payments-prod",
"Status": "Updating",
"Health": "Grey",
"Tier": { "Name": "WebServer", "Type": "Standard" }
}
# Rolling config update; log groups appear under /aws/elasticbeanstalk/payments-prod/
# DeleteOnTerminate=false keeps the logs even after the environment is torn down.

One update-environment call sets all three options; RetentionInDays should match your documented audit window.

Beanstalk log streaming under the hooddeep dive

Log streaming is controlled entirely through the aws:elasticbeanstalk:cloudwatch:logs configuration option namespace. Three options matter: StreamLogs (boolean, default false) turns streaming on; RetentionInDays (default 7) sets how long CloudWatch keeps the events before expiring them — the allowed values mirror CloudWatch's own retention enum (1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653); and DeleteOnTerminate (default false) decides whether the log groups are deleted when the environment is terminated. For an audit trail you almost always want DeleteOnTerminate=false so the evidence outlives the environment.

When StreamLogs is enabled, Beanstalk installs the CloudWatch Logs agent on each instance and creates log groups named under /aws/elasticbeanstalk/<environment-name>/, with separate streams for eb-activity.log, the proxy server access logs (nginx or Apache), and the platform-specific application logs. The streaming is per-instance, so when auto-scaling adds a node its logs flow into the same group automatically, and when it removes a node the already-streamed events remain in CloudWatch even though the instance and its EBS volume are gone. That decoupling of log lifetime from instance lifetime is the entire point of the control.

The instance profile attached to the environment must carry logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents, and logs:DescribeLogStreams. The AWS-managed AWSElasticBeanstalkWebTier and AWSElasticBeanstalkWorkerTier policies already include these, so if your environment uses the default service-role setup no IAM change is needed; if you've replaced the managed policy with a custom one, add those actions or the agent silently fails to publish.

# Verify the instance profile can actually publish to CloudWatch Logs.
# Find the instance profile attached to the environment.
aws elasticbeanstalk describe-configuration-settings \
  --application-name payments --environment-name payments-prod \
  --query 'ConfigurationSettings[].OptionSettings[?OptionName==`IamInstanceProfile`].Value' \
  --output text

# Confirm the role grants the logs:* actions the agent needs.
aws iam list-attached-role-policies \
  --role-name aws-elasticbeanstalk-ec2-role \
  --query 'AttachedPolicies[].PolicyName' --output table

# Watch the log groups appear after enabling StreamLogs.
aws logs describe-log-groups \
  --log-group-name-prefix /aws/elasticbeanstalk/payments-prod/ \
  --query 'logGroups[].[logGroupName,retentionInDays]' --output table

What is the impact of not streaming Beanstalk logs?

The primary impact is an audit-trail gap. Frameworks like PCI DSS (clause 10.4.2 specifically, which this control maps to), SOC 2, HIPAA, and ISO 27001 all require that log records be retained for a defined period and be available for review. Elastic Beanstalk's instances are ephemeral by design — auto-scaling, instance replacement, and managed platform updates all destroy instances routinely — so without streaming, your effective log retention is 'until the next scaling event,' which could be hours. That is almost never long enough to satisfy a control that assumes days or months.

The second impact is incident response. When something goes wrong — a spike in 5xx errors, a suspected intrusion, a customer disputing a transaction — the access logs and application logs are the first place responders look. If the instance that served the traffic has since been recycled, those logs are gone, and the investigation stalls before it starts. You're left reconstructing events from CloudTrail and metrics alone, which tells you what API calls happened but not what your application actually did.

The third impact is the silent-failure nature of the gap. Nothing breaks when streaming is off. The application serves traffic, dashboards look green, and the only symptom is an empty /var/log on instances that no longer exist — which you discover only when you go looking, typically under pressure during an audit or an incident. By then the window to capture the missing data has long closed; you cannot retroactively recover logs from a terminated instance.

The cost side is genuinely minor and worth naming so it never becomes an excuse: CloudWatch Logs ingestion runs about $0.50 per GB and storage about $0.03 per GB-month in US-East, so a typical web environment streaming activity and access logs costs a few dollars a month, less if you set a sensible retention period. The trade is a few dollars against a failed audit or a blind incident response — which makes this one of the cheapest High-severity findings to remediate.

How do you remediate ElasticBeanstalk.3 safely?

Remediation is a four-step loop: confirm the current setting, verify the instance profile can publish, enable streaming with the right retention and teardown behaviour, then bake it into your environment templates so new environments are compliant from day one.

1. Inspect the current cloudwatch:logs configuration

Read the aws:elasticbeanstalk:cloudwatch:logs namespace on the environment with describe-configuration-settings. Check StreamLogs (the control fails if it's false), RetentionInDays (default 7 is rarely enough for an audit window), and DeleteOnTerminate (default behaviour you'll want to override to keep evidence). Do this per environment across every account and region in scope — the finding is per-environment.

2. Verify the instance profile has CloudWatch Logs permissions

The EC2 instance profile attached to the environment needs logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents, and logs:DescribeLogStreams. The managed AWSElasticBeanstalkWebTier and AWSElasticBeanstalkWorkerTier policies already grant these, so default setups need no change. If you run a custom instance role, add these actions first — otherwise streaming silently fails to publish and the control stays failed even after you flip StreamLogs.

3. Enable streaming with a deliberate retention and teardown policy

Set StreamLogs=true, choose a RetentionInDays from the allowed enum that matches your audit obligation (90 is a common PCI-aligned choice), and set DeleteOnTerminate=false so the log groups survive when the environment is later destroyed. Apply with a single update-environment call; Beanstalk performs a rolling configuration update with no application downtime, and the log groups appear under /aws/elasticbeanstalk/<env>/ within minutes.

4. Bake it into .ebextensions so new environments inherit it

Add the three options to a .ebextensions/*.config file (or a Saved Configuration) in source control so every environment created from that application is compliant at launch. This converts the control from a recurring per-environment chore into a one-time template change, and it means a freshly spun-up environment never spends time in a failed state waiting for someone to remember the manual step.

# .ebextensions/cloudwatch-logs.config — commit this so every new env is compliant.
cat > .ebextensions/cloudwatch-logs.config <<'EOF'
option_settings:
  aws:elasticbeanstalk:cloudwatch:logs:
    StreamLogs: true
    DeleteOnTerminate: false
    RetentionInDays: 90
EOF

# Or remediate an existing environment in place with one call.
aws elasticbeanstalk update-environment \
  --environment-name payments-prod \
  --option-settings \
    Namespace=aws:elasticbeanstalk:cloudwatch:logs,OptionName=StreamLogs,Value=true \
    Namespace=aws:elasticbeanstalk:cloudwatch:logs,OptionName=DeleteOnTerminate,Value=false \
    Namespace=aws:elasticbeanstalk:cloudwatch:logs,OptionName=RetentionInDays,Value=90

# Confirm the log groups now exist before closing the finding.
aws logs describe-log-groups \
  --log-group-name-prefix /aws/elasticbeanstalk/payments-prod/ \
  --query 'logGroups[].logGroupName' --output table

Quick quiz

Question 1 of 5

An Elastic Beanstalk environment fails ElasticBeanstalk.3. You enable streaming by setting StreamLogs=true but leave the other options at their defaults. What's the remaining risk?

You've completed Stream Elastic Beanstalk logs to CloudWatch. You now know why Beanstalk's ephemeral instances make local log files unreliable, exactly what ElasticBeanstalk.3 checks, and how to remediate it through the aws:elasticbeanstalk:cloudwatch:logs namespace — setting StreamLogs, a deliberate RetentionInDays, and DeleteOnTerminate=false, then baking it into .ebextensions so it never recurs. Next time this finding appears, you'll have a one-command fix and a template change that closes it for good.

Back to the library