Hit the 6MB Lambda payload limit? Here’s what you can do.

So you have built a serverless application, that, amongst other things, lets you upload images and files to S3.

The setup is very simple: API Gateway, Lambda and S3.

It took you no time to implement and it works like a dream. You pat yourself on the back for another job well done.

One day, a customer complained that he couldn’t upload pictures of his cat to your app.

You check your Lambda logs and you see an error right away.

Execution failed: 6294149 byte payload is too large for the RequestResponse invocation type (limit 6291456 bytes)

What the hell?

You google the error message and soon realise that you’ve hit the 6MB invocation payload limit for synchronous Lambda invocations.

https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html [1]

Turns out you can’t POST more than 6MB of data to Lambda through API Gateway.

What do you do now?

Well, two options pop to mind.

Option 1: use API Gateway service proxy

You can remove Lambda from the equation and go straight from API Gateway to S3 using API Gateway service proxies.

To learn more about API Gateway service proxies and why you should use them, please read my previous post [2] on the topic.

This approach doesn’t require any client changes. If you’re using the Serverless [3] framework then the serverless-apigateway-service-proxy [4] plugin makes it easy to configure this S3 integration.

custom:
  apiGatewayServiceProxies:
    - s3:
        path: /upload/{fileName}
        method: post
        action: PutObject
        bucket:
          Ref: S3Bucket
        key:
          pathParam: fileName
        cors: true

The problem with this approach is that you’re limited by the API Gateway payload limit of 10MB.

https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html [5]

I mean, it’s more than 6MB, but not by that much that you no longer have to worry about hitting it.

Option 2: use presigned S3 URL

You can also rearchitect your application slightly so that uploading images becomes a two-step process:

  1. The client makes an HTTP GET request to API Gateway, and the Lambda function generates and returns a presigned S3 URL [6].
  2. The client uploads the image to S3 directly, using the presigned S3 URL.

This approach requires you to update both the client and the server. But it’s a simple code change on both sides.

Since the client will upload the files to S3 directly, you will not be bound by payload size limits imposed by API Gateway or Lambda. And with presigned S3 URLs, you can do this securely without having to open up access to the S3 bucket itself.

Option 3: Lambda@Edge to forward to S3

Thank you to Timo Schilling for this idea.

First, set up a CloudFront distribution and point it to an invalid domain.

Then attach a Lambda@Edge function to perform any authentication and authorization as necessary, and redirect valid requests to the S3 bucket using a presigned S3 URL.

Compared to option 2, this approach is much more developer-friendly for the caller. As far as the caller is concerned, it’s just a plain cost POST HTTP endpoint, which is great when you’re working with 3rd party/external developers.

However, using Lambda@Edge brings some operational overhead:

  • Updates to Lambda@Edge functions take a few minutes (should be around 5 minutes following recent improvements to CloudFront deployment times) to propagate to all AWS regions.
  • Lambda@Edge functions actually execute in a region closest to the edge location, not at the edge location itself. And the kicker is that their logs would be sent to CloudWatch Logs in that same region. So if you need to monitor your Lambda@Edge functions, then you need to check the logs in ALL the regions where they could have run. And if you’re ingesting Lambda logs to a centralised logging platform (e.g. logz.io) then you’d need to set up the ingestion process in all of these regions too.

Option 4: use presigned POST instead

Thank you to Zac Charles for this idea [7].

This is similar to option 2, but you can also specify a POST Policy [8] to restrict the POST content to a specific content type and/or size. There are also some other differences to option 2. For example, you need to use an HTTP POST instead of PUT. And you need to pass in a number of HTTP headers returned by the createPresignedPost request.

Zac’s post has a lot more detail about caveats you need to look out for, please go and give that a read.

Wrap up

So there you have it. The 6MB Lambda payload limit is one of those things that tend to creep up on you as it is one of the less talked about limits.

Generally speaking, I prefer option 2 as it eliminates the size limit altogether. At the expense of requiring changes to both the frontend and backend.

If your application needs to impose some size limit on the payload in the first place, then it might not be the right solution for you. API Gateway and Lambda’s payload size limit is a built-in defensive mechanism for you in that case. You can enable WAF with API Gateway, which can enforce even more granular payload limits without having to implement them in your own code (and maintain that code).

I hope you’ve found this post useful. If you want to learn more about running serverless in production and what it takes to build production-ready serverless applications then check out my upcoming workshop, Production-Ready Serverless [9]!

In the workshop, I will give you a quick introduction to AWS Lambda and the Serverless framework, and take you through topics such as:

  • testing strategies
  • how to secure your APIs
  • API Gateway best practices
  • CI/CD
  • configuration management
  • security best practices
  • event-driven architectures
  • how to build observability into serverless applications

and much more!

Links

[1] Lambda quotas and limits

[2] The why, when and how of AWS API Gateway service proxies

[3] Serverless framework

[4] serverless-apigateway-service-proxy plugin for the Serverless framework

[5] API Gateway quotas and limits

[6] S3 sharing objects with presigned URLs

[7] S3 Uploads — Proxies vs Presigned URLs vs Presigned POSTs

[8] S3 Post Policy for presigned POST

[9] Production-Ready Serverless workshop