Java Serverless on AWS Lambda · Frameworks, Native Images and SDK Clients

Spring Boot on Lambda

Why a Spring Boot function takes eight seconds to start, and the options for running Spring well on Lambda.

Scenario

Example app: a team packages an existing Spring Boot 3 REST service into a Lambda function using the AWS Serverless Java Container adapter. It has 60 beans, Spring Data JPA, Actuator and auto-configuration for several unused features.

What you see in production

Cold starts take 6 to 8 seconds; API Gateway sometimes times out the first request after a scale-out. Warm requests are fast. Memory use is high, so the function is configured with 3 GB.

The naive code

public class StreamLambdaHandler implements RequestStreamHandler {
    private static final SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler =
        SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);   // full context at init

    @Override
    public void handleRequest(InputStream in, OutputStream out, Context ctx) throws IOException {
        handler.proxyStream(in, out, ctx);
    }
}

Reproduce it

Start-up cost of a Spring context, by option (illustrative)
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<String, int[]> options = new LinkedHashMap<>();
        // {cold start ms, warm p50 ms}
        options.put("full Spring Boot app, no SnapStart", new int[]{7200, 12});
        options.put("trimmed auto-config + lazy init", new int[]{3900, 12});
        options.put("trimmed app + SnapStart + priming", new int[]{650, 12});
        options.put("Spring Cloud Function, functional beans", new int[]{2200, 10});
        options.put("Spring native image (GraalVM)", new int[]{350, 15});
        options.forEach((name, v) -> System.out.printf("%-42s cold %5d ms   warm %3d ms%n", name, v[0], v[1]));
    }
}

Output

full Spring Boot app, no SnapStart         cold  7200 ms   warm  12 ms
trimmed auto-config + lazy init            cold  3900 ms   warm  12 ms
trimmed app + SnapStart + priming          cold   650 ms   warm  12 ms
Spring Cloud Function, functional beans    cold  2200 ms   warm  10 ms
Spring native image (GraalVM)              cold   350 ms   warm  15 ms

Run this on your computer — see “Get Started” for setup.

Root cause

Spring Boot builds its application context at start-up: classpath scanning, auto-configuration evaluation, proxy generation, JPA metamodel and connection pool initialisation. On a long-lived server this happens once per deployment; on Lambda it happens on every cold start, multiplied by scale-out.

The fix

Choose deliberately, often in combination:

  • SnapStart with priming: keep Spring, snapshot the initialised context, re-establish connections in afterRestore. Usually the biggest win with the least code change.
  • Trim the application: exclude unused auto-configurations, enable lazy initialisation where acceptable, avoid heavy features (JPA with large metamodels) in latency-sensitive functions.
  • Spring Cloud Function with functional bean definitions for event-driven functions, avoiding the web stack entirely.
  • Spring native images (GraalVM via Spring Boot's AOT support) on a custom runtime for the fastest start-up, at the cost of longer builds and reflection configuration.
  • Run the web app unchanged with the Lambda Web Adapter, or on a container platform if the service has steady traffic.

Trade-offs

  • SnapStart keeps the developer experience but requires care with connections and uniqueness.
  • Native images start fastest but need build infrastructure and testing of reflection-heavy libraries.
  • A "Lambda-lith" Spring app shares warm environments across routes; splitting into small functions reduces start-up per function but duplicates shared code.

Detect it in production

  • Init Duration or Restore Duration per version; API Gateway 504s on first requests.
  • Spring start-up reports (spring.main.log-startup-info, ApplicationStartup with BufferingApplicationStartup) to find slow beans.
  • Memory headroom from REPORT lines.

Key points

  • Spring Boot initialisation runs on every cold start.
  • SnapStart + priming is often the best first step.
  • Trim auto-configuration, consider Spring Cloud Function or native images.

The Spring Boot course covers the framework itself; this lesson focuses on running it on Lambda.

Further reading: AWS Serverless Java Container · Spring Cloud Function: AWS Lambda · Lambda SnapStart

Practice questions

Why are Spring Boot cold starts on Lambda so slow?

Answer

The application context is built at start-up: classpath scanning, auto-configuration, proxies, JPA initialisation and connection pools. Lambda repeats this for every new execution environment.

What is usually the most effective first step to speed up a Spring Boot function?

Answer

Enable SnapStart with priming in a beforeCheckpoint hook and re-establish connections after restore; then trim unused auto-configuration. This keeps the codebase largely unchanged.

When would Spring Cloud Function be a better fit than running the full web stack on Lambda?

Answer

For event-driven functions (SQS, EventBridge, S3) or simple request handlers, where functional bean definitions avoid the servlet and web layers and reduce start-up work.

Open in the app to mark lessons complete, take notes and track your progress.