A Java app that answers in milliseconds during the day and takes many seconds after a quiet hour is showing a cold start. Two separate things can cause it, and each has its own fix: the platform stopped the app because nobody used it, or the JVM needs time to start and warm up. Find out which one you have before you change anything.
Plan rules checked in November 2026.
Sleep or startup? Tell them apart
Look at the platform's event log around a slow request. If the app was stopped and started again just before it, the delay is sleep plus a full startup. If the app kept running and the slow request came right after a deploy or restart, the cause is JVM warm-up alone.
Free tiers are the usual source of sleep. In the plans we compare, Render's free web services spin down after 15 minutes without traffic, Koyeb's free instance scales to zero after an hour idle, and Heroku Eco sleeps after 30 minutes. Each wake-up means a complete JVM start while the visitor waits.
Measure the startup time
Spring Boot prints how long the application took to start in its log. Note that number on the host, not on your laptop: a small shared CPU can make the same app several times slower to start. Everything below is judged against this one measurement, so take it before and after each change.
Measure from the outside
The log tells you how long the JVM took to start; the visitor experiences more than that, including the platform waking the instance and routing the request. Measure what the visitor sees with curl after the app has been idle:
curl -s -o /dev/null -w "connect %{time_connect}s first byte %{time_starttransfer}s total %{time_total}s\n" https://your-app.example.com/
Run it once after a quiet period and once immediately after. The difference between the two runs is the cold start. If the first run is much slower than the startup time in the log, most of the delay is the platform waking the instance, and only an always-on plan removes it. If both numbers are close, the JVM startup is the part to work on.
Spring Boot can also report which beans take longest to start. With BufferingApplicationStartup configured and Actuator's startup endpoint exposed, the app lists each startup step with its duration, which shows where lazy initialization or removing a dependency will help most.
Start less at startup
Many apps create objects at startup that the first request does not need. Spring Boot can postpone creating beans until they are used:
spring.main.lazy-initialization=true
This shortens startup and moves part of the cost to the first request that touches each bean, so test the result. Beyond that, remove starters and libraries the app does not use, avoid scanning large packages, and move heavy work, such as warming caches, out of the startup path or into a background task.
Use the AOT cache on Java 25
Recent JDKs can save part of the startup work and reuse it. The Ahead-of-Time cache, introduced in JDK 24, stores artifacts from a training run, such as loaded and linked classes. The Java 25 documentation shows how a training run writes the cache:
java -XX:AOTCacheOutput=app.aot -jar app.jar
Run the app through typical requests during this training run, then start it in production with the cache:
java -XX:AOTCache=app.aot -jar app.jar
The cache must be created with the same JDK and the same JAR it is used with, so build it as part of your image. The gain depends on the app; compare the startup time from the log with and without it.
Choose a plan that stays awake
When users must never wait, no startup tuning can remove the time a sleeping app needs to wake up. An always-on plan removes the sleep entirely. The free Java hosting page shows each free tier's sleep rule, and the Java Launchpad switches to always-on plans when you tell it the app has real users.
Repeated cold starts can also come from the app being killed and restarted for memory, which looks like slowness from the outside. If the event log shows restarts without idle time, check the exit code 137 page.
Sources