Java is Back on Lambda: Building a Sub-Second GenAI API with Spring Boot 3, SnapStart, and Bedrock

Published: (December 16, 2025 at 08:10 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Cover image for Java is Back on Lambda: Building a Sub-Second GenAI API with Spring Boot 3, SnapStart, and Bedrock

Is Java too slow for AWS Lambda? For years, the answer was “yes, mostly” due to the dreaded cold starts. Today, with Java 21 and SnapStart, the answer is “absolutely not”.

In this post I show how I built a production‑grade serverless API using Spring Boot 3, Java 21, and AWS Bedrock (Claude 3.5) that starts in under 500 ms.

The Problem: The “Cold Start” Tax

If you’ve run Java on Lambda before, you know the pain. The JVM is heavy; loading classes, initializing the Spring context, and setting up AWS SDKs can take 5 to 15 seconds.

  • Asynchronous background jobs can tolerate this.
  • Synchronous APIs (e.g., chatbots or REST endpoints) cannot.

The Solution: AWS Lambda SnapStart

SnapStart changes the game by using CRaC (Coordinated Restore at Checkpoint).

  1. AWS starts your function during the deployment phase.
  2. It runs the full initialization (JVM warm‑up, Spring context, dependency injection).
  3. It takes a memory snapshot of the initialized Firecracker microVM.
  4. It caches this snapshot.

When a user invokes the API, Lambda simply restores the memory state—like waking a laptop from hibernation rather than booting it cold.

The Architecture

The project integrates Generative AI (AWS Bedrock) via a Spring Cloud Function.

Key components

ComponentDetail
RuntimeJava 21 (AWS Corretto)
FrameworkSpring Boot 3.2 + Spring Cloud Function
InfrastructureTerraform
AI ModelAnthropic Claude 3.5 Sonnet (via AWS Bedrock)

The Code: Optimization Techniques

1. Smart Initialization (Constructor Injection)

Heavy work (creating the Bedrock client) is moved to the constructor, so SnapStart captures it during deployment, not during invocation.

@Service
public class BedrockService {
    private final BedrockRuntimeClient bedrockClient;

    public BedrockService() {
        // CRITICAL: runs during the "Deployment" phase, not the "Invocation" phase!
        this.bedrockClient = BedrockRuntimeClient.builder()
            .region(Region.US_EAST_1)
            // Use the lightweight HTTP client instead of Netty
            .httpClient(UrlConnectionHttpClient.builder().build())
            .build();
    }

    // ... business logic ...
}

Pro tip: Replacing the default Netty HTTP client with UrlConnectionHttpClient reduces artifact size and start‑up time, which is critical for Lambda performance.

2. Terraform Configuration

Enabling SnapStart is a one‑liner, but you must also enable version publishing.

resource "aws_lambda_function" "java_snapstart_function" {
  function_name = "java-bedrock-poc"
  runtime       = "java21"
  handler       = "org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest"

  # ... other config ...

  publish = true  # REQUIRED for SnapStart

  snap_start {
    apply_on = "PublishedVersions"
  }

  environment {
    variables = {
      # Tune JVM for fast tier‑1 compilation
      JAVA_TOOL_OPTIONS = "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
    }
  }
}

The Benchmark: Did It Work?

I deployed the function and tested it with the AWS CLI. The difference is night and day.

MetricWithout SnapStartWith SnapStart 🚀
Init Duration~8,000 ms0 ms (cached)
Restore DurationN/A~350 ms
Execution~1,500 ms~1,500 ms
Total User Wait~9.5 seconds 🐢~1.8 seconds 🚀

Note: Execution time includes the call to Claude 3.5 (GenAI). The Java Lambda overhead dropped to sub‑second levels.

Conclusion

Java is no longer a second‑class citizen in the serverless world. By combining Spring Boot 3, SnapStart, and lightweight clients, we can build enterprise‑grade, strongly typed, and testable applications that perform as well as Node.js or Python.

For senior architects handling legacy migrations, this provides a viable path to move complex monoliths to AWS without rewriting everything in a new language.

Source Code

The full working example (including Terraform code and specialized shell scripts) is available on GitHub:

github.com/jmontagne/poc_java_lambda_snapstart_bedrock

Back to Blog

Related posts

Read more »