In modern cloud environments, startup time matters more than ever. Platforms like Google Cloud Run, AWS Lambda, and Azure Functions are often billed by the second, and their performance is directly affected by cold starts. A slow startup can mean higher costs for you and longer wait times for your users — both of which can make a real difference in production.
Slow startups can also cause functional issues. For example, in the company where I work, it’s common for Google Pub/Sub to trigger a retry during the startup of Spring applications, because the first message is perceived as failed. This means the same message ends up being processed twice. If it weren’t for the project’s idempotency mechanisms, such errors could easily lead to catastrophic consequences.
Quarkus has built a strong reputation for lightning-fast startup times, often positioning itself as the go-to choice for serverless and containerized deployments. Spring Boot, on the other hand, is still the heavyweight champion of the Java ecosystem, but it’s often criticized for being slower to start.
But is Spring Boot really that far behind?
In this post, we’re going beyond theory and marketing claims. We’ll run real-world, side-by-side benchmarks, measuring startup times for both frameworks under default settings and after applying every optimization trick we can find. By the end, you’ll see not only which framework starts faster — but also how much that difference actually matters in cost, reliability, user experience, and practical deployments.
Test Environment & Methodology
All benchmarks were run on the same machine to ensure a fair comparison:
CPU: Intel Core i5-8400H @ 2.5GHz
RAM: 32 GB DDR4 (2667 MT/s)
GPU: 2 GB - GeForce MX130
OS: Windows 11 Pro
JDK: OpenJDK 21
Build Tools: Gradle 8.14.X
Spring boot version: 3.5.4
Quarkus version: 3.25.2
Measurement Tool: Custom startup time measurement script that records the time from process start until the application responds to a health check endpoint. You can check the code in the Appendix (end of this page).
I will run three main scenarios:
- Spring Boot JAR vs Quarkus JAR - Both projects built with defaults, no optimizations.
- Optimized Spring Boot JAR vs Optimized Quarkus JAR - Minimal configurations, unused dependencies removed, lazy initialization, and other startup optimizations applied.
- Spring Boot Native Image vs Quarkus Native Image - Direct native-to-native comparison, compiled with GraalVM.
Each test will be executed five times, and we will calculate the average startup time for consistency.
Spring boot JAR x Quarkus JAR
For the first test, both frameworks were built using their default configurations — no tuning, no lazy initialization, and no native compilation. The goal here was to see how they perform “out of the box” in a fair, apples-to-apples comparison.
Both applications expose the health check endpoint, and were measured using the custom startup script described earlier. The numbers below represent the average over five consecutive runs for each framework.
Note: All measurements are in milliseconds and reflect the time from process start until the application responds with HTTP 200 on its health endpoint.
Results:
Spring:
Quarkus:
On average, both Spring Boot and Quarkus achieved ~110 ms startup times in this default JAR mode. This was closer than many might expect, especially given Quarkus’s strong marketing around JVM startup speed.
Conclusion: While Quarkus is often perceived as significantly faster in cold start scenarios, this test shows that, in default JVM mode, Spring Boot can keep up when running on a modern machine with no additional load. The real differences may start to show when we apply optimizations and explore native compilation in the next tests.
Optimized Spring Boot JAR vs Optimized Quarkus JAR
For this second test, I applied common startup optimizations to both frameworks, including lazy initialization, disabling the startup banner, reducing logging levels, and excluding unused auto-configurations. The goal was to see how much these tweaks could improve startup times compared to the default builds.
However, because this test was performed on a very simple application — essentially a default project with no external dependencies, databases, or messaging systems — the optimizations made very little difference in the measured startup times.
This is expected, since in minimalistic applications:
- Most of the startup time is spent initializing the core framework and embedded server, which leaves little room for improvement.
- Lazy initialization benefits scenarios with many beans, but here there are only a few.
- Excluding unused auto-configurations has limited effect when those configurations aren’t actively loaded.
Therefore, in this case, both optimized Spring Boot and optimized Quarkus JARs showed startup times very similar to their default counterparts.
That said, in real-world applications with multiple dependencies, database connections, caches, and complex service layers, these optimizations can have a much greater impact.
I plan to revisit this comparison in the future using a more realistic microservice example, which will better highlight the benefits of these optimizations.
Conclusion: For simple projects, startup optimizations may not yield significant gains, but as applications grow in complexity, applying these techniques becomes essential to reduce cold start latency and improve responsiveness in cloud environments.
Spring Boot Native Image vs Quarkus Native Image
Finally, we ran a direct comparison between the native builds of both frameworks.
Results:
Quarkus:
Spring:
Both native builds achieved an average startup time of around 95 ms, resulting in a virtual tie in performance.
Despite the similar metric, there is an important difference in how these native builds are created and executed:
- Quarkus produces a standalone native executable that runs directly on the operating system without requiring a JVM or container.
- Spring Boot produces a Docker image that runs a native executable inside a container, adding an extra layer of abstraction and management, but still maintaining very fast startup performance.
Why are native builds faster?
Native builds reduce startup time because GraalVM compiles the application to native code, eliminating the need for the JVM to initialize, perform JIT (Just-In-Time) compilation, and other heavy runtime processes.
So native builds brought about a 13.6% improvement in startup time compared to the default JARs.
Final Conclusion
For small applications with few dependencies and low complexity, the difference in startup time between Spring Boot and Quarkus — whether running on the JVM or as native builds — is practically negligible.
Both frameworks deliver fast startup times that should not significantly impact cost or user experience in serverless or containerized environments.
However, as applications grow in complexity, the optimizations and specific advantages of each framework may become more pronounced — something I plan to explore in future comparisons with real-world, complex scenarios.
Script Used:
#!/bin/bash
APP_COMMAND=$1 APP_PORT=${2:-8080} RUNS=${3:-5}
if [ -z "$APP_COMMAND" ]; then echo "Usage: ./measure_startup.sh "start_command" [port] [runs]" echo "Example: ./measure_startup.sh "java -jar target/myapp.jar" 8080 5" exit 1 fi
echo "Measuring startup time..." echo "Command: $APP_COMMAND" echo "Port: $APP_PORT" echo "Runs: $RUNS" echo
for i in $(seq 1 $RUNS); do echo "Run $i..."
START_TIME=$(date +%s%3N)
eval $APP_COMMAND > /dev/null 2>&1 &
APP_PID=$!
until curl -s http://localhost:$APP_PORT/actuator/health > /dev/null 2>&1 || \
curl -s http://localhost:$APP_PORT/q/health > /dev/null 2>&1; do
sleep 0.0005
done
END_TIME=$(date +%s%3N)
TOTAL_TIME=$((END_TIME - START_TIME))
echo "Startup time: ${TOTAL_TIME} ms"
kill $APP_PID > /dev/null 2>&1
sleep 5
done