Skip to content
← Writing
Technical guideAWS CDK9 min

AWS CDK S3 to Lambda via EventBridge: the cost model and failure paths

AWS CDK S3 EventBridge Lambda trigger costs: native S3 events are effectively free and CloudTrail data events bill per event. When to use each one.

By Rahul LadumorUpdated October 27, 2026
On this page

When AWS Cost Explorer shows AWS CloudTrail as a bigger line than AWS Lambda or Amazon S3 on an S3-triggered workload, the event path is a likely cause. A common aws cdk s3 eventbridge lambda trigger pipeline pushes every S3 object event through CloudTrail data events into Amazon EventBridge, and pays per event for what is, in most designs, free plumbing.

This guide is for teams building that pattern with the AWS Cloud Development Kit (AWS CDK). You get the cost of each layer, when CloudTrail is worth paying for, and the failure paths a cost calculator does not show.


Uploads land in a KMS-encrypted S3 bucket that sends Object Created events to the EventBridge default bus, which invokes a VPC-placed Lambda function. A dashed path shows the costlier CloudTrail data-events route. The function reads Secrets Manager and Parameter Store through VPC endpoints.

Who this is for

If you run, or are about to build, an S3-triggered serverless workflow in CDK TypeScript where the Lambda function is placed in an Amazon VPC, encrypted with AWS Key Management Service (AWS KMS), and the events need to fan out to more than one target, this is for you. You should be comfortable reading a CDK stack and you should know how IAM, VPC endpoints and KMS behave. I am not going to explain what a Lambda function is.


Audit or eventing: the choice that sets the bill

There are two different ways to get an S3 object event into EventBridge, and people often mix them up.

One: native S3 to EventBridge. Since late 2021, S3 can send event notifications straight to the default EventBridge bus. You flip one setting on the bucket (eventBridgeEnabled: true on the s3.Bucket in CDK), and every Object Created, Object Deleted, and similar event lands in EventBridge with full rule matching and fan-out to as many targets as you want.

The S3 to EventBridge events themselves are not billed as custom events. For the eventing path, this is effectively free.

Two: CloudTrail data events into EventBridge. You enable S3 data events on a CloudTrail trail, and EventBridge matches on the CloudTrail event stream. This was the only way to get rich S3 eventing into EventBridge before the native integration existed, and a lot of stacks, and a lot of older blog posts, still reach for it by reflex.

CloudTrail data events cost $0.10 per 100,000 events (CloudTrail pricing, checked June 2026). That per-event charge is the CloudTrail line in the Cost Explorer view above.

Turning on CloudTrail data events is an audit decision. Triggering a Lambda function is an eventing decision. If you want a durable, queryable record of every object-level API call for compliance or forensics, data events are how you get it, and they are worth the money. If all you want is "run this Lambda when an object lands," routing through CloudTrail means you are paying for an audit trail you may not have asked for, and using it as a trigger.

So before you look for savings, ask whether you are paying for audit or for eventing. I expect most teams that route S3 through CloudTrail wanted eventing and bought audit by accident.


What CloudTrail data events cost at 1M and 10M PUTs a day

When you do route through CloudTrail, the cost scales directly with object operations, and it is easy to undercount because teams rarely model it before shipping.

At $0.10 per 100,000 events:

  • 1 million PUTs/day = 10 units of 100k = $1/day, about $30/month just for the trail.
  • 10 million PUTs/day = 100 units = $10/day, about $300/month.

For a bucket taking 10 million object writes a day, that is roughly $300/month in CloudTrail data event charges. The Lambda function doing the work runs about 300 million times a month, which is about $1,310 at 512 MB and 500 ms average, about $60 of it request charges. So the trail adds roughly a quarter to the compute bill and does none of the work. That is the moment to ask whether you needed CloudTrail in this path at all, because native S3 to EventBridge would have moved those same events for nothing.


VPC endpoint, KMS and log costs that sit outside Lambda

The trail is the obvious one. More cost hides in services people do not associate with this pipeline.

VPC endpoints. A Lambda function in a VPC that reads from AWS Secrets Manager and AWS Systems Manager (SSM) Parameter Store privately needs interface endpoints. Each interface endpoint is $0.01 per hour per AZ, about $7.30/month/AZ (PrivateLink pricing, checked June 2026), plus $0.01/GB processed.

Deploy to 3 AZs for production and one endpoint is about $22/month. You usually need at least S3 (gateway endpoint, free), Secrets Manager, and SSM, so the two interface endpoints run about $44/month across 3 AZs, or about $29 across 2, before data charges.

Teams undercount this because it shows up on the bill under "VPC," not "Lambda."

KMS API calls. Customer-managed keys charge $0.03 per 10,000 requests after the first 20,000/month free, plus $1/month per key (KMS pricing, checked June 2026). S3 decrypts on GET and encrypts on PUT, Lambda decrypts environment variables on cold start, CloudWatch Logs encrypts each batch.

Without an S3 Bucket Key, SSE-KMS makes a KMS request for every object: GenerateDataKey when the object is written and Decrypt when the Lambda function reads it. The environment-variable decrypt happens once per cold start, not per invocation. At low volume this is a couple of dollars and you ignore it. At 5 million objects a month it is about 10 million requests, roughly $30/month, and it grows with every object. That is the kind of number that surprises you because nothing in the design points at it.

An S3 Bucket Key removes most of it. S3 derives per-object data keys from a short-lived bucket-level key instead of calling KMS for each object, and AWS puts the reduction in KMS requests at up to 99 percent, depending on how many requesters there are and how requests cluster in time.

Amazon CloudWatch Logs retention. A log group with no expiry keeps paying storage forever.

This stack sets retention explicitly, which is correct. The trap is that if anyone recreates the Lambda by hand outside CDK, the new log group defaults to never-expire and the cost control quietly breaks.


Five changes that cut the bill safely

Not all of this is load-bearing. Some of it trims without touching the core design.

Turn on an S3 Bucket Key. In CDK it is bucketKeyEnabled: true on a bucket with BucketEncryption.KMS. It applies to new objects only; existing objects keep calling KMS until they are copied. The encryption context becomes the bucket ARN, so check key policies and IAM conditions that match on an object ARN before you turn it on.

Drop CloudTrail from the eventing path. If you are using data events only to trigger Lambda, switch to native S3 to EventBridge and the data event bill goes to zero. Keep a CloudTrail trail for audit if you need one, but stop using it as your trigger. This is the single biggest line item on most of these stacks, and for pure eventing it is avoidable.

If you keep CloudTrail for audit, scope the selectors. You rarely need every object operation on every prefix. Scope the data event selector to the exact prefix and operations the workload cares about (s3://your-bucket/incoming/, PutObject only). On a mixed-workload bucket that can remove most of the data event volume; check your own split of object operations by prefix before you count on it.

Right-size VPC endpoints by AZ. If the workload is not customer-facing, 2 AZs instead of 3 saves about $7.30/month per endpoint per dropped AZ. For dev and staging, a single NAT Gateway can be cheaper than a full set of interface endpoints at low traffic. Run the numbers for your call volume.

Set log retention aggressively. 7 days covers most operational debugging, 90 days if security review needs it. Never-expire is almost never the right answer.


Other S3 event paths and when each one fits

Native S3 to EventBridge. The default for almost everyone now. Free eventing, rule-based routing, fan-out to many targets, no bucket-notification single-target limit.

This is what the CloudTrail path was working around before it existed. If you are starting fresh, start here.

Direct S3 event notification to Lambda (or Amazon SNS or Amazon SQS). Lowest latency, sub-second, zero eventing cost. The limit is that two notification configurations on one bucket cannot overlap on the same event type and prefix, so sending one event to several consumers needs SNS in the middle, and you get no audit record. Fine for a single-consumer pipeline, awkward past that.

S3 to SQS to Lambda. Underrated for high-throughput work that does not need EventBridge filtering. SQS gives you batching, a dead-letter queue, and visibility-timeout semantics, and the cost is predictable.

I would reach for this on cost-sensitive pipelines because the SQS dead-letter queue keeps failed events for reprocessing.

It looks old next to EventBridge. It is often the correct call.

Amazon S3 Batch Operations. Different use case, worth naming: if you are processing objects that already exist rather than reacting to new uploads, Batch Operations is far cheaper than a continuous event pipeline. $0.25 per job plus $1 per million object operations.

CloudTrail data events into EventBridge. Now a narrow choice. It earns its cost only when you need object-level audit records and want to route on them: management-event context, cross-account trails, or a compliance requirement that the eventing and the audit be the same record. For plain "run this when an object lands," it is the expensive answer to a question native EventBridge already solved.


Monthly cost at three traffic levels

Rough monthly numbers for a workload that has kept CloudTrail in the path, using the prices above. Assumptions: 30-day month, one Lambda invocation per PUT, two interface endpoints, and SSE-KMS without a Bucket Key (two KMS requests per object). Data processing charges on the endpoints are left out.

Scenario Monthly CloudTrail Monthly VPC Endpoints Monthly KMS Total
100k PUTs/day, 2 AZ, scoped selectors ~$3 ~$29 ~$18 ~$50
1M PUTs/day, 3 AZ, full data events ~$30 ~$44 ~$180 ~$254
10M PUTs/day, 3 AZ, full data events ~$300 ~$44 ~$1,800 ~$2,144

With one Lambda invocation per PUT at 512 MB and 500 ms average, Lambda costs about $13, $131 and $1,310 a month for these three rows, before the free tier. So the event pipeline costs almost four times the compute at the top row, where the fixed endpoint charge dominates. At the higher rows the KMS column, not CloudTrail, is the biggest line, and at the bottom row it passes the compute bill itself.

Both fixes are configuration. Switching to native S3 to EventBridge removes the CloudTrail column, about $300/month at the bottom row, with no architectural loss as long as you did not need the audit trail. Turning on an S3 Bucket Key takes most of the KMS column with it. Neither one touches the Lambda function.


Failure paths a cost calculator misses

A few things that do not show up in a cost calculator.

Latency. CloudTrail data events through EventBridge add a few seconds end to end versus direct notification, because the event has to land in the trail first.

For a compliance pipeline processing audit records, fine. For a user-facing feature showing a "processing" spinner, not fine. Native S3 to EventBridge is faster than the CloudTrail path here too, which is one more reason it wins for eventing.

The trail going dark. If you do route through CloudTrail and the trail gets disabled, or the destination bucket policy changes, EventBridge silently stops receiving S3 events.

There is no DLQ on that path and no native alert on event-delivery failure. You find out when someone notices the Lambda has not run in hours.

Put a CloudWatch alarm on EventBridge FailedInvocations and separately monitor that the trail is active.

KMS key deletion. Delete the customer-managed key (CMK) or let its key policy drift, and S3 cannot decrypt objects, Lambda cannot read environment variables, and CloudWatch Logs cannot write, all at once, with no graceful degradation. Set the key deletion window to the maximum 30 days, tag the key, and test the key policy in staging before it surprises you in production.


When cutting CloudTrail goes too far

After a large CloudTrail bill, it is tempting to swing too hard the other way.

Ripping CloudTrail out to save $30/month is correct only if you do not need the audit trail. The moment a real requirement lands, such as a GDPR deletion you have to prove, a SOC 2 control or a breach inquiry, you will want that object-level record, and recreating history after the fact is impossible. Move CloudTrail out of the trigger path, by all means. Decide separately, and on the record, whether you still want it as an audit trail.

Same caution on VPC placement and KMS. Pulling the Lambda out of the VPC to dodge endpoint costs means Secrets Manager and SSM traffic leaves the private network. Collapsing to a single shared CMK to cut complexity is fine until the day you have to audit every policy touching that key at once.

A cheaper design still has a cost. It just moves somewhere less visible.


Where to start: three checks that cost nothing

Open Cost Explorer and look at whether CloudTrail and KMS are line items on this workload. If CloudTrail is, the fastest win is to ask whether the trail is feeding your trigger or your audit.

If it is the trigger, move to native S3 to EventBridge and watch the data event charge disappear. If it is audit, scope the selectors to the prefix and operations you actually need.

If KMS is, check whether the bucket has an S3 Bucket Key, and enable it for new objects once the key policy is ready. Then check your VPC endpoint count against the AZs you require. Those three checks cost nothing, cover most of the savings on this pattern, and none of them weakens the architecture.


Sources

Pages were read on 2026-10-18 unless noted.

AWS pricing pages

Other AWS documentation

Next step

Have the same problem on your stack?

Send the architecture, AWS bill concern, deploy pain, or GenAI reliability issue. I'll find the first real bottleneck and propose a small, reversible fix.

FAQ

Does sending S3 events to EventBridge cost anything?

S3 events sent natively to the default EventBridge bus are not billed as custom events, so the eventing path is effectively free. You turn it on with one bucket setting. Lambda, KMS and VPC endpoint charges still apply.

How much do CloudTrail data events cost for S3?

CloudTrail data events cost $0.10 per 100,000 events. At 1 million PUTs a day that is about $30 a month, and at 10 million PUTs a day about $300 a month.

Should I use CloudTrail or native S3 to EventBridge to trigger Lambda?

Use native S3 to EventBridge when all you need is to run a Lambda function when an object lands. CloudTrail data events earn their cost only when you need object-level audit records and want to route on them. You can keep a trail for audit and still trigger from native events.

Can one S3 event notification trigger more than one Lambda function?

Direct S3 event notifications allow one target per event type per bucket, so fan-out needs Amazon SNS in the middle. Native S3 to EventBridge has no bucket-notification single-target limit and can fan out to many targets.

Why did my S3-triggered Lambda stop running with CloudTrail and EventBridge?

If the trail is disabled or its destination bucket policy changes, EventBridge stops receiving S3 events, and that path has no DLQ and no native alert. Put a CloudWatch alarm on EventBridge FailedInvocations and separately monitor that the trail is active.

Related reading

#AWS CDK#S3#EventBridge#Lambda#Cost#IaC
Rahul Ladumor

Rahul Ladumor

Principal Cloud & AI Platform Architect. AWS Professional certified, 5x AWS Community Builder. I work with teams that have real users, real AWS bills, and real production pressure.

About Rahul →

Scroll or pinch to zoom, drag to move100%