Spring Boot DevTools
While you build an application, you change code dozens of times an hour. Stopping the application, starting it again and refreshing the browser after every change adds up to a lot of waiting. Spring Boot DevTools removes most of that waiting: it restarts your application automatically when code changes, refreshes the browser for you, and switches on settings that make development easier.
This tutorial shows how to add DevTools, how its fast restart works, how to set up your IDE, how to control what triggers a restart, and how to make sure it never reaches production.
1. What Is Spring Boot DevTools?
Spring Boot DevTools is an optional module, spring-boot-devtools, that adds features for development only:
| Feature | What it does for you |
|---|---|
| Automatic restart | Restarts the application within seconds when compiled code changes |
| LiveReload | Refreshes the browser automatically when pages, CSS or JavaScript change |
| Development defaults | Turns off template and resource caching and shows more error details |
| Global settings | Lets you keep personal DevTools settings in one file for all projects |
| Remote support | Can push changes to an application running on another machine |
DevTools switches itself off when the application runs as a packaged production JAR, so it is safe to keep in your project.
2. Why You Need DevTools
| Without DevTools | With DevTools |
|---|---|
| Stop the app, start it again, wait for it to boot | Save the file; the app restarts by itself in a second or two |
| Template changes need a restart because pages are cached | Template changes show on the next browser refresh |
| Refresh the browser by hand after every CSS change | LiveReload refreshes it for you |
| Hunt for hidden error details | Error responses include the message and details during development |
Suppose a full start takes 10 seconds and you restart 60 times a day. That is 10 minutes of waiting, every day, just for restarts. DevTools restarts are typically much faster, and they happen without you having to do anything.
3. Adding DevTools to Your Project
For Maven, add the dependency as runtime and optional:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
For Gradle, use the special developmentOnly configuration:
dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}
| Setting | Why it is there |
|---|---|
runtime scope | Your code never refers to DevTools classes, so it is not needed for compiling |
optional (Maven) | Stops DevTools from passing on to other projects that depend on yours |
developmentOnly (Gradle) | Available when running locally, but never packaged into the final JAR |
No configuration is needed. Run the application from your IDE or with ./mvnw spring-boot:run, and DevTools is active.
4. Automatic Restart
DevTools watches the classpath, the folders your IDE or build tool compiles into. When a file there changes, the application restarts automatically.
4.1 Why the restart is fast: two classloaders
A Java classloader is the part of the JVM that loads classes. DevTools splits your application across two of them:
| Classloader | Loads | On restart |
|---|---|---|
| Base classloader | Classes that do not change: Spring, Hibernate, Jackson and all other library JARs | Kept as it is |
| Restart classloader | Your own classes and resources, the ones you are editing | Thrown away and created again |
Because the large libraries stay loaded, only your own code is loaded again, which is why a DevTools restart is usually much quicker than a cold start.
4.2 What triggers a restart
- Changed Java classes, once they are compiled.
- Changed configuration files on the classpath, such as
application.propertiesorapplication.yml.
By default, files in static/, public/, templates/, resources/, META-INF/resources/ and META-INF/maven/ do not trigger a restart. Those changes are picked up without one, because DevTools turns caching off.
4.3 See it work
Start this controller, then change the message and save:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello from version 1"; // change to "version 2" and save
}
}
The log shows the restart, using a thread named restartedMain:
[ restartedMain] c.j.demo.DemoApplication : Started DemoApplication in 0.6 seconds
Call /hello again and you get the new message. If you see restartedMain in the log, DevTools restart is active.
5. Setting Up Your IDE
DevTools restarts only after your code is compiled. How that happens depends on your IDE:
| IDE | What to do |
|---|---|
| IntelliJ IDEA | Enable Settings → Build, Execution, Deployment → Compiler → Build project automatically, and Settings → Advanced Settings → Allow auto-make to start even if developed application is currently running. Or press Build → Build Project (Ctrl+F9 / ⌘F9) after changes. |
| Eclipse / Spring Tools | Works out of the box: saving a file compiles it automatically |
| VS Code | Works out of the box with the Java extension, which compiles on save |
| Command line | Run ./mvnw spring-boot:run and recompile in another terminal with ./mvnw compile |
If saving a file does not restart the application, the code is almost always not being compiled yet. Check the IDE setting first.
6. Restart vs Hot Reload
DevTools restarts the application. It does not swap code into the running JVM. These are different techniques:
| Technique | How it works | Keeps state in memory? | Tools |
|---|---|---|---|
| Automatic restart | Throws away your classes and starts the application context again | No | Spring Boot DevTools |
| Debugger hot swap | Replaces method bodies in the running JVM | Yes | Any IDE debugger, but only for changes inside existing methods |
| Advanced hot reload | Reloads classes, including new methods and fields, without a restart | Yes | JRebel (commercial), HotswapAgent with a special JDK |
For most Spring Boot work, DevTools restart is the simplest and most reliable choice. Your application always starts from a clean state, exactly as it would in production.
7. Controlling What Triggers a Restart
Fine-tune restart behavior in application.properties:
# Add exclusions to the defaults (static/, templates/ and so on remain excluded)
spring.devtools.restart.additional-exclude=reports/**,exports/**
# Watch an extra folder that is not on the classpath
spring.devtools.restart.additional-paths=../shared-config
# How often to look for changes, and how long to wait for more changes before restarting
spring.devtools.restart.poll-interval=1s
spring.devtools.restart.quiet-period=400ms
| Property | Meaning |
|---|---|
spring.devtools.restart.enabled | Turn automatic restart on or off (LiveReload and other features stay on) |
spring.devtools.restart.exclude | Replaces the default exclusion list |
spring.devtools.restart.additional-exclude | Adds to the default exclusion list; usually the one you want |
spring.devtools.restart.additional-paths | Extra folders to watch for changes |
spring.devtools.restart.poll-interval | Time between checks for changed files |
spring.devtools.restart.quiet-period | Waits this long without further changes, so a burst of saves causes only one restart |
spring.devtools.restart.trigger-file | Restart only when this specific file changes |
7.1 Restart only when you say so
If your IDE compiles continuously, the application may restart more often than you like. A trigger file puts you in control: DevTools then restarts only when that one file changes:
spring.devtools.restart.trigger-file=.reloadtrigger
Create an empty .reloadtrigger file on the classpath, for example in src/main/resources, and update it whenever you want a restart. Some IDEs can do this with one click.
7.2 Switching restart off completely
To disable restart regardless of any configuration file, set a system property before the application starts:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(DemoApplication.class, args);
}
}
8. LiveReload
DevTools starts a small LiveReload server on port 35729. When a browser is connected to it, the page refreshes automatically after a restart or after changes to HTML templates, CSS or JavaScript.
- Install a LiveReload extension in your browser.
- Start your application with DevTools.
- Open your page and switch the extension on for that tab.
- Change a template or stylesheet: the page refreshes by itself.
LiveReload is most useful for server-rendered pages, for example with Thymeleaf. If you build a separate front end with a tool like Vite or the Angular CLI, that tool usually has its own automatic refresh, and you can turn LiveReload off:
spring.devtools.livereload.enabled=false
9. Development-Friendly Defaults
Some Spring Boot defaults are right for production but awkward during development. DevTools quietly changes a number of them while it is active:
| Setting changed by DevTools | Effect during development |
|---|---|
| Template caching off (Thymeleaf, FreeMarker, Groovy templates, Mustache) | Template edits appear on the next refresh, without a restart |
| Static resource caching off | Updated CSS, JavaScript and images are served immediately |
| Error details included (message, binding errors, stack trace) | Error responses show what went wrong |
| H2 console enabled | Browse an in-memory H2 database at /h2-console |
| Persistent HTTP sessions | Logged-in sessions can survive a restart |
These changes apply only while DevTools runs, so production keeps its safe, fast defaults. To switch them off and use your own settings, set spring.devtools.add-properties=false.
10. Global Settings for All Projects
Some DevTools settings are personal preferences, such as a trigger file or turning LiveReload off. Rather than adding them to every project, put them in one global file in your home folder:
spring.devtools.restart.trigger-file=.reloadtrigger
spring.devtools.livereload.enabled=false
These settings apply to every Spring Boot application you run on your machine that uses DevTools, and they never end up in a shared repository. A .yml or .yaml file in the same folder also works. Older versions used ~/.spring-boot-devtools.properties, which is still read when the newer folder does not exist.
11. Remote DevTools
DevTools can also update an application running on another machine, such as a development server, by sending your changed classes to it over HTTP. Both sides need setup:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- keep DevTools inside the packaged JAR -->
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
spring.devtools.remote.secret=a-long-random-secret-value
On your own machine, run org.springframework.boot.devtools.RemoteSpringApplication from the IDE, with the remote URL as the program argument, for example https://dev.example.com. Changes you compile locally are then pushed to the remote application, which restarts.
Use this with great care. Anyone who knows the secret can change the code of the running application. Only enable it on trusted development servers, always over HTTPS, and never on production.
12. Keeping DevTools Out of Production
DevTools disables itself automatically in two ways:
- It is not packaged. The Spring Boot Maven plugin leaves DevTools out of the final JAR by default, and Gradle’s
developmentOnlyconfiguration is never packaged. - It turns itself off when the application is started with
java -jaras a fully packaged application, even if DevTools were somehow included.
You can confirm that it is gone from the JAR:
./mvnw clean package
jar tf target/demo-0.0.1-SNAPSHOT.jar | grep devtools # prints nothing: not packaged
Also check the production startup log: the main thread should be named main, not restartedMain.
13. Troubleshooting
| Problem | Likely cause | Fix |
|---|---|---|
| Saving a file does nothing | The IDE is not compiling automatically | Enable automatic build, or build manually (section 5) |
| Restarts happen constantly | A tool keeps writing files to the classpath, such as generated sources or logs | Exclude that folder with additional-exclude, or use a trigger file |
| Two restarts for one change | The IDE saves several files a moment apart | Increase spring.devtools.restart.quiet-period |
ClassCastException: X cannot be cast to X | The same class was loaded by both classloaders | Tell DevTools which JARs belong to the restart classloader (see FAQ) |
| Template changes do not appear | A custom template setting re-enables caching | Remove cache=true settings from development properties |
No restartedMain in the log | DevTools is not on the classpath, or the app runs as a packaged JAR | Check the dependency and run from the IDE or spring-boot:run |
14. Best Practices
- Add DevTools as
optional/runtimein Maven ordevelopmentOnlyin Gradle, so it never reaches production. - Set up automatic compiling in your IDE, so restarts happen on save.
- Use
additional-excluderather thanexclude, to keep the sensible default exclusions. - Keep personal preferences in the global settings file, not in shared project files.
- Use a trigger file if automatic restarts interrupt your flow.
- Do not rely on in-memory state surviving restarts; use a database, even an embedded one, for test data.
- Avoid Remote DevTools except on trusted development servers, and never in production.
15. Advantages and Limitations
| Advantages | Limitations |
|---|---|
| Much faster feedback after code changes | Restart, not true hot reload: in-memory data is lost |
| No manual stop and start | Needs automatic compiling in some IDEs |
| Template and static changes show instantly | Rare classloader issues with some libraries |
| Helpful error details and the H2 console during development | LiveReload needs a browser extension |
| Safe by default: off in packaged production JARs | Remote DevTools carries security risks |
16. Summary
Spring Boot DevTools makes development faster with almost no setup. Add one dependency, set your IDE to compile automatically, and your application restarts within moments of every change, thanks to its two-classloader design. LiveReload refreshes the browser, development-friendly defaults turn off caching and show error details, and a global file keeps your personal preferences. DevTools stays out of production on its own; confirm it with a quick check of your packaged JAR.
17. Frequently Asked Questions (FAQ)
Q1. Will DevTools end up in my production JAR?
Not with the standard setup. The Spring Boot Maven plugin removes DevTools from the packaged JAR by default, and Gradle never packages the developmentOnly configuration.
Even if it were packaged, DevTools turns itself off when the application starts as a fully packaged JAR with java -jar. The only way to keep it active is to deliberately set excludeDevtools to false, which you should do only for Remote DevTools on a trusted server.
Q2. Why doesn’t my application restart when I save a file in IntelliJ IDEA?
DevTools watches compiled classes, and IntelliJ does not compile on save by default. Either build manually with Build → Build Project (Ctrl+F9 / ⌘F9), or turn on automatic building:
- Settings → Build, Execution, Deployment → Compiler: tick Build project automatically.
- Settings → Advanced Settings: tick Allow auto-make to start even if developed application is currently running.
After that, saving a change compiles it within a moment, and DevTools restarts the application.
Q3. Does a DevTools restart keep my data and logged-in users?
Partly:
- In-memory data is lost. Maps, lists, caches and anything else held in your beans start empty again, because the application context is recreated.
- HTTP sessions can survive. DevTools enables persistent sessions, so a logged-in user often stays logged in. The objects stored in the session must be
Serializable. - Database data is kept in a real database. An in-memory H2 database is lost, unless you load sample data at startup.
A data.sql file or a small CommandLineRunner that inserts sample data makes restarts painless.
Q4. How do I fix “ClassCastException: X cannot be cast to X” with DevTools?
This strange-looking error means one class was loaded twice, once by each of DevTools’ two classloaders, so Java sees two different types with the same name. It usually involves a library from your own company, such as a shared module of entity classes, opened in the IDE as a project.
Tell DevTools to load that library with the restart classloader too. Create this file in your application:
# src/main/resources/META-INF/spring-devtools.properties
restart.include.shared-model=/shared-model-[\\w\\d-\\.]+\\.jar
The value is a pattern matching the JAR’s file name. There is also restart.exclude.* for the opposite case.
Q5. Is DevTools active when I run my tests?
Restart is switched off when DevTools detects a test framework such as JUnit, so tests are never interrupted by a restart.
However, the development-friendly default properties, such as disabled caching, may still apply when DevTools is on the test classpath. If a test depends on production-like settings, set them explicitly in the test properties, or set spring.devtools.add-properties=false.
Q6. Do template changes cause a restart?
No. Files under templates/, static/ and public/ are excluded from restart on purpose. They do not need one: DevTools turns off template and resource caching, so the next page load simply reads the new file.
This makes front-end work especially quick. Edit a Thymeleaf page or a stylesheet, refresh the browser, or let LiveReload do it, and the change is there, with no restart at all.
Q7. Do I need to restart manually after changing dependencies in pom.xml?
Yes. DevTools only reloads your own classes; library JARs live in the base classloader, which is kept across restarts. A new or upgraded dependency therefore needs a full restart:
- Reload the Maven or Gradle project in your IDE.
- Stop the application completely.
- Start it again.
The same applies to changes in JVM options and environment variables.
Q8. What do the poll interval and quiet period mean?
| Setting | Default | Meaning |
|---|---|---|
poll-interval | 1s | How often DevTools checks the classpath for changed files |
quiet-period | 400ms | How long there must be no further changes before a restart begins |
Compiling often writes many files one after another. The quiet period waits for that burst to finish, so you get one restart instead of several. If you still see double restarts, increase it:
spring.devtools.restart.poll-interval=2s
spring.devtools.restart.quiet-period=1s
The poll interval must always be longer than the quiet period.
Q9. What is the “condition evaluation delta” in the log after a restart?
After each restart, DevTools logs which auto-configurations changed compared with the previous start. For example, after you add a database setting, it shows that the DataSource auto-configuration now applies.
This is a quick way to see the effect of a configuration change. If it clutters your log, switch it off:
spring.devtools.restart.log-condition-evaluation-delta=false
Q10. How do I pause automatic restarts without removing DevTools?
Choose the option that fits:
- For one project:
spring.devtools.restart.enabled=falseinapplication.properties. LiveReload and the development defaults keep working. - For one run: start with
-Dspring.devtools.restart.enabled=falseas a JVM option in your IDE’s run configuration. - Restart only on demand: configure a trigger file (section 7.1).
Q11. Does DevTools work inside a Docker container?
Not out of the box, and usually it is not worth it. DevTools needs compiled classes on the container’s classpath to change, and a typical container runs a packaged JAR, in which DevTools is switched off.
Common, simpler approaches:
- Run the application on your machine with DevTools, and run only the databases and other services in Docker, for example with Docker Compose.
- Use Spring Boot’s Docker Compose support (
spring-boot-docker-compose), which starts those services automatically when the app starts.
Remote DevTools can push changes into a container, but it adds setup and security concerns.
Q12. What is the difference between DevTools and Spring Boot’s Docker Compose support?
| DevTools | Docker Compose support | |
|---|---|---|
| Module | spring-boot-devtools | spring-boot-docker-compose |
| Helps with | Your own code: fast restarts and LiveReload | External services: starting databases, brokers and caches |
| How | Watches the classpath and restarts | Runs docker compose up and connects the app to the services |
They solve different problems and work well together: Docker Compose support starts PostgreSQL for you, and DevTools restarts your code as you change it.
Q13. Is Remote DevTools safe to use?
Only in limited conditions. Remote DevTools lets anyone who knows the secret replace the code of the running application. That is very powerful and, therefore, dangerous.
- Use it only on development servers that you trust and control.
- Always use HTTPS, so the secret and your classes are not sent in plain text.
- Use a long, random secret, and do not commit it to source control.
- Never enable it on production or on anything reachable from the internet.
Q14. Does DevTools work with Spring WebFlux and other non-MVC applications?
Yes. Automatic restart works for any Spring Boot application, whether it uses Spring MVC, WebFlux, or has no web layer at all, such as a command-line tool or a message consumer.
LiveReload and the template-related defaults only matter for applications that serve web pages. For other applications, DevTools is still useful just for the fast restart.
Q15. Why does my application keep restarting in a loop?
Something keeps writing files into a watched folder, and each write triggers another restart. Common causes:
- The application itself writes files, such as logs, uploads or exports, into
src/main/resourcesor the build output folder. - A code generator, for example for OpenAPI clients, runs on every build and rewrites its output.
- A front-end build writes into the classpath continuously.
Write runtime files outside the project, or exclude their folder:
spring.devtools.restart.additional-exclude=uploads/**,generated/**
Q16. Does DevTools slow down my application while I develop?
Only slightly. DevTools checks for file changes about once a second, which costs very little. The development defaults, such as disabled caching, make each page request a little slower, because templates and files are read again every time.
That trade-off is intended for development. Never judge your application’s real speed with DevTools active; measure performance with a packaged JAR, as production runs it.
Q17. Can I use LiveReload without installing a browser extension?
The classic way requires a LiveReload extension, and such extensions are not available or maintained for every browser. Alternatives:
- Many IDEs, such as IntelliJ IDEA Ultimate, can refresh the browser on their own when files change.
- Front-end dev servers such as Vite or webpack have built-in instant refresh, which suits single-page applications better.
- Simply press refresh: thanks to disabled caching, template and CSS changes are visible immediately.
If you do not use LiveReload, turn it off with spring.devtools.livereload.enabled=false to free port 35729.
Q18. Is DevTools the same as “hot reload” in other frameworks?
Not exactly. Many front-end frameworks swap changed code into the running page while keeping its state. DevTools instead restarts the Spring application context quickly, so in-memory state is reset.
In practice the result feels similar: you save, and the change is live a moment later. The restart approach has an advantage, too: every restart begins from a clean state, so you never debug a confusing mix of old and new code.
Q19. Should every developer on the team use the same DevTools settings?
Split them into two groups:
| Kind of setting | Where to put it | Example |
|---|---|---|
| Needed by the project | application.properties (shared) | additional-exclude for a generated folder |
| Personal preference | Global file in your home folder | Trigger file, LiveReload off |
This way, the project works well for everyone, while each developer keeps their preferred workflow without changing shared files.
Q20. How can I tell whether DevTools is really active?
Three quick checks:
- The thread name. Startup log lines show
restartedMaininstead ofmain. - The LiveReload message. The log mentions that the LiveReload server is running on port 35729, unless you turned it off.
- A quick test. Change a string in a controller, compile, and see whether the application restarts by itself.
If none of these happen, check that the dependency is present and that you run the application from the IDE or with spring-boot:run, not from a packaged JAR.