I often notice people wondering if and how it’s possible to run Spring Boot on AWS Lambda Functions. I understand this because I know developers in the Java ecosystem often use the Spring Framework. And since more and more people built Lambda functions using Java, the question will be asked at some point. It’s definitely possible to run Spring Boot on AWS Lambda. However, if you think about it a second time, you’ll find many reasons why it is a bad idea. This blog post discusses the advantages and disadvantages of running Spring Boot on AWS Lambda on a conceptual level. And in case I won’t convince you, I’ll at least provide you some hints to overcome the disadvantages.
Are you using Java or even Spring (Boot) on AWS Lambda? I have used it in production systems and I believe it’s good for certain use cases like background data processing. What is your use case? Do you try to overcome the disadvantages and if so, how? Just add a comment below or mention me on Twitter 👍
As you all probably know (if not, then read here), AWS Lambda and similar Function-as-a-Service (FaaS) solutions let you run code in the cloud without managing any of the underlying infrastructure. This enables a cloud provider to better scale your code because these functions are a small enough to be started on demand. Although Java is one of the supported languages with a slower initial start time (= cold start), it can still be a good choice for AWS Lambda because of its big ecosystem that grew over all the years.
For example, the Spring Framework is one of the most popular Java frameworks. It collects best practices working with different things like a database and HTTP. Eventually Spring Boot made it even simpler to connect the different Spring modules together. A typical use case for Spring Boot is to build a microservice that can handle HTTP requests. You just define a
@Controller , give it a
@RequestMapping and have your first endpoint ready that can react to an HTTP request (read this blog post as an example).
Unfortunately this setup won’t work out of the box if you’re running this code on AWS Lambda. The reason is that AWS Lambda is using an event-based mechanism. An event can consist of any kind of data that is serializable. However, you won’t be able to receive HTTP requests from within your Lambda function. (Of course you can make HTTP requests to other services but not the other way around). This is the reason why you need a service or application in front of your AWS Lambda function. Such a service can receive HTTP requests and forward them to your Lambda function. A typical example is an AWS API Gateway. But you can also use an AWS Application Load Balancer. These services keep the HTTP connection open until your Lambda function responds to the HTTP event. Without this setup you won’t receive any HTTP events in your Lambda function.
In order to start using Spring Boot on AWS Lambda, I suggest you to checkout the different helpers in aws-serverless-java-container. This is a collection of Java classes that help you using typical Java frameworks on AWS Lambda, including Spring. They already provide you with the necessary glue code to connect the AWS Lambda handler with Spring Boot. There are also other (but similar) ways to ingrate the Spring Framework like using Spring Cloud Functions on AWS Lambda. In the end, the architecture looks similar to this:
Let’s discuss the advantages of this approach because there are definitely a few. First, you can setup an API Gateway to forward all HTTP requests as events using a proxy integration with AWS Lambda. This basically enables you to configure a wildcard path in API Gateway and lets your Spring Boot app handle all the internal routing. Using this approach, you and your team can continue using Spring Boot like you’re used to. Second, as soon as one Lambda function instance has initialized the Spring Boot container, this function will respond in a consistent way. The performance is as expected from Java, at least that’s my experience with Java Lambda functions.
However, the most obvious advantage is that you don’t have to manage the underlying instances with AWS Lambda. But this advantage has a catch: You need to ensure you are not storing any session data (or similar) inside your Spring Boot app. Remember that you should keep Lambda functions stateless, otherwise scaling becomes harder.
You might have recognized already that using API Gateway + AWS Lambda + Spring Boot is duplicating responsibilities. In the world before Serverless you would have used Spring Boot alone to handle HTTP requests and implement your business logic. Now the combination of API Gateway and AWS Lambda is replacing it. Even more than that, it enables you to have better scalability because of this separation of concerns from an operational aspect. This means you can put a big question mark on the idea of running Spring Boot inside AWS Lambda.
But not only duplicating responsibilities is a bad sign. Also the fact that you add unnecessary complexity to integrate Spring Boot with AWS Lambda should make you think twice. In the end, all these wrapper and helper classes introduce more potential for bugs. And together with Spring Boot they also have a bad effect on the cold start of your Lambda functions of course. Apart from that, Spring Boot own it’s own is even too slow to start quickly like it’s expected from Lambda functions. This means, you’ll have massive spikes in your response times when a new Lambda function instance is started.
Although not worrying about the instances of your Lambda function sounds appealing, you have to remember that you can not control how many you’ll have. This can have negative consequences if you’re e.g. making requests to an SQL database in direct response to external events. You might run out of database connections quickly if a spike hits your Lambda function because AWS Lambda spins up more and more instances.
I (and many other Serverless developers) recommend to keep AWS Lambda functions simple and small. If you follow this recommendation, then your functions are a lot easier to scale. If you instead build another monolith using Spring Boot on top of AWS Lambda, then you are doing it wrong. Keep that in mind while developing AWS Lambda functions with Java but especially if you include Spring as well.
Having said that, I have successfully used Spring in Java Lambda functions in previous projects. I enjoyed running them in the background doing some data processing. However, I’m not convinced that they are a good choice for “customer facing” endpoints like a REST API. The only exception is if you keep a minimum of Lambda instances running. But then you can question if AWS Lambda is the right choice for your Spring Boot app. In such a case I’d rather suggest you to use traditional EC2 instances or ECS.
If you still want to use Sprint Boot on AWS Lambda, then you should consider some recent developments. For example, you might have heard of GraalVM or Quarkus. You can use it to run Java native images on AWS Lambda by providing a custom runtime. Also, Spring is adding support for GraalVM native images. I can recommend looking into the Slides of Vadym’s talk “Adopting Java for the Serverless world” that goes into more detail.