Codersee
Kotlin on the backend
Codersee
Kotlin on the backend
In this, short quide, I will show you how to enable the development mode and auto-reload feature in Ktor application.
Together, we will learn how to enable the Ktor development mode in several ways and how auto-reload can help with the Kotlin HTML DSL app example.
If you prefer video content, then check out my video:
As the first step, let’s quickly prepare a basic Ktor application responding with HTML.
To do so, let’s navigate to the https://start.ktor.io page and configure the artifact first:
The choice is up to you here. From my end, I will be using the latest Ktor version with the YAML config and without the version catalog.
Then, let’s add the Kotlin HTML DSL plugin necessary for our simple example:
Lastly, let’s download the zip package, extract it, and import it to our IDE.
If you are working in IntelliJ IDEA, then please make sure that you use JDK 23, too.
You can do that in two places:
- Settings -> Build, Execution, Deployment -> Build Tools -> Gradle. Here, please set the Gradle JVM to 23
- Project Structure -> Project Settings -> Project. On this page, please set the SDK to 23 and Language level to “SDK default”
After all, please sync the gradle project and wait for the process to finish.
With all of that done, let’s delete the unwanted files that were generated automatically, like Routing.kt
, or Templating.kt
and expose the endpoint:
fun Application.module() { routing { get("/") { call.respondHtml { body { + "Hello world!" } } } } }
As we can see, whenever we reach the root path (by default, the localhost:8080
), we should see the “Hello world!” text.
Now, let’s change the text, for example, to contain only a “Hello!” message.
What happens if we refresh the page? Nothing! The page does not reflect our code change until we restart the server manually!
And, especially when dealing with HTML, it may become a pretty tedious task.
It won’t be a big surprise if I say that the Ktor auto-reload feature may be a great solution here? 😉
But what is that?
Well, long story short, it its a feature that we can turn on by enabling the development mode in Ktor. And when enabled, it reloads application classes on code changes.
To be more specific, according to the documentation, Ktor listens for the changes to the following files:
/build/classes/kotlin/main/META-INF /build/classes/kotlin/main/com/your-package-namn /build/classes/kotlin/main/com /build/classes/kotlin/main /build/resources/main
And as we can see, by the code changes, we mean the generated outputs and artifacts. But the good news is that with gradle, we can easily automate the generation, too.
Lastly, I just wanted to mention that application logs are also pretty clear about what we need to do in order to enable auto-reload:
2025-03-25 06:40:49.929 [main] INFO Application - Autoreload is disabled because the development mode is off. 2025-03-25 06:40:50.072 [main] INFO Application - Application started in 0.374 seconds. 2025-03-25 06:40:50.363 [DefaultDispatcher-worker-2] INFO Application - Responding at http://127.0.0.1:8080
With all of that said, let’s get back to the practice part.
As the first step, we must enable the development mode. And we can do that in a few ways, so let me walk you through them.
When we navigate to the build.gradle.kts
file, we should see the following:
application { mainClass = "io.ktor.server.netty.EngineMain" val isDevelopment: Boolean = project.ext.has("development") applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") }
So the easy and the dummy approach would be to simply hardcode the value- -Dio.ktor.development=true
:
application { mainClass = "io.ktor.server.netty.EngineMain" applicationDefaultJvmArgs = listOf("-Dio.ktor.development=true") }
Now, when we run the app with ./gradlew run
, we will see that the message disappeared, proving the development mode is enabled:
2025-03-25 07:10:15.883 [main] INFO Application - Application started in 0.41 seconds. 2025-03-25 07:10:16.142 [main] INFO Application - Responding at http://127.0.0.1:8080 <==========---> 83% EXECUTING [1m 13s]
But first, this approach does not work if you run the server in IntelliJ by using the green icon because, by default, it does not invoke any gradle command.
Secondly, it is hardcoded, meaning we don’t have too much control over it in various environments.
Another option is to bring back what we already had in the build.gradle.kts
:
val isDevelopment: Boolean = project.ext.has("development") applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
And from now on, the only thing we need is the -Pdevelopment
flag for the gradlew run
command:
./gradlew run -Pdevelopment
It’s clearly better and gives us more flexibility.
Another option that we have on the table is to directly use the VM option we saw above- -Dio.ktor.development=true
.
For that purpose, we can even completely get rid of the previous snippet from application
in build.gradle.kts
:
application { mainClass = "io.ktor.server.netty.EngineMain" }
And now, whenever invoking the gradlew run
command, we will simply put the whole arg:
./gradlew "-Dio.ktor.development=true" run
Moreover, when working with IntelliJ, we can edit our configuration to pass the same option:
So we can see that this option will work with both gradle, as well as the IntelliJ IDEA runs.
Lastly, we can also set that in the application.yaml
:
ktor: development: true application: modules: - com.codersee.ApplicationKt.module deployment: port: 8080
It works exactly the same, and with environment variables can give us a lot of flexibility, too.
Alright, so at this point, our app is up and running with the development mode ON.
However, when we make any code change, nothing still happens!
Well, as I mentioned previously, the Ktor auto-reload feature looks for the output files. Meaning we must build the project to see the changes.
And we can do that by either running the ./gradlew build
or by navigating to the Build tab in IntelliJ:
And although this works and we can now see changes without the restart, it is still not too convenient.
Well, the good news we can slightly adjust the gradlew build command and automate that:
./gradlew -t build -x test
With the -t
flag (or --continuous
), we enable the continuous build mode. Gradle will watch for changes in the source files, and whenever a relevant change is detected, it will automatically rerun the build task.
Additionally, we exclude the test
task using the -x
flag to speed up the process a little 🙂
If we would like to add that as a run configuration to IntelliJ, then it is really easy, too:
And basically, that’s all! At this point, we can add the necessary HTML changes without restarting the Ktor server.
As I mentioned above, Ktor listens for the file updates in the following directories:
/build/classes/kotlin/main/META-INF /build/classes/kotlin/main/com/your-package-namn /build/classes/kotlin/main/com /build/classes/kotlin/main /build/resources/main
If we would like to narrow down the list of watched directories, then we can do that, for example, in the YAML config file:
ktor: deployment: watch: - resources
The only thing we must specify is the part of the watched patch that we want to keep.
And that’s all for this quick tutorial on how to enable the auto-reload feature in Ktor.
If you found this content useful, then check out my Ktor Server Pro course– the most comprehensive Ktor course on the market.
Thank you for being here, and see you around! 🙂