Ktor 2.3.8 Help

Creating a static website

In this series of tutorials, we'll show you how to create a simple blog application in Ktor:

  • First, we'll show how to host static content like images and HTML pages.

  • In the next tutorial, we'll make our application interactive using the FreeMarker template engine.

  • Finally, we'll add persistence to our website using the Exposed framework.

Ktor journal


Before starting this tutorial:

Create a new Ktor project

To create a base project for our application using the Ktor plugin, open IntelliJ IDEA and follow the steps below:

  1. On the Welcome screen, click New Project.

    Otherwise, from the main menu, select File | New | Project.

  2. In the New Project wizard, choose Ktor from the list on the left. On the right pane, specify the following settings:

    New Ktor project
    • Name: Specify a project name.

    • Location: Specify a directory for your project.

    • Build System: Make sure that Gradle Kotlin is selected as a build system.

    • Website: Leave the default example.com value as a domain used to generate a package name.

    • Artifact: This field shows a generated artifact name.

    • Ktor version: Choose the latest Ktor version.

    • Engine: Leave the default Netty engine.

    • Configuration in: Choose HOCON file to specify server parameters in a dedicated configuration file.

    • Add sample code: Disable this option to skip adding sample code for plugins.

    Click Next.

  3. On the next page, add the Routing, Static Content, and Freemarker plugins:

    Ktor plugins

    Click Create and wait until IntelliJ IDEA generates a project and installs the dependencies.

Examine the project

To look at the structure of the generated project, let's invoke the Project view:

Initial project structure


First, let's open the build.gradle.kts file and examine added dependencies:

dependencies { implementation("io.ktor:ktor-server-core-jvm") implementation("io.ktor:ktor-server-freemarker-jvm") implementation("io.ktor:ktor-server-netty-jvm") implementation("ch.qos.logback:logback-classic:$logback_version") testImplementation("io.ktor:ktor-server-tests-jvm") testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") }

Let's briefly go through these dependencies one by one:

  • ktor-server-core-jvm adds Ktor's core components to our project.

  • ktor-server-freemarker-jvm allows us to use the FreeMarker template engine, which we'll use to create the main page of our journal.

  • ktor-server-netty-jvm adds the Netty engine to our project, allowing us to use server functionality without having to rely on an external application container.

  • logback-classic provides an implementation of SLF4J, allowing us to see nicely formatted logs in a console.

  • ktor-server-test-jvm and kotlin-test-junit allow us to test parts of our Ktor application without having to use the whole HTTP stack in the process.

Configurations: application.conf and logback.xml

The generated project also includes the application.conf and logback.xml configuration files located in the resources folder:

  • application.conf is a configuration file in HOCON format. Ktor uses this file to determine the port on which it should run, and it also defines the entry point of our application.

    ktor { deployment { port = 8080 port = ${?PORT} } application { modules = [ com.example.ApplicationKt.module ] } }

    If you'd like to learn more about how a Ktor server is configured, check out the Configuration in a file help topic.

  • logback.xml sets up the basic logging structure for our server. If you'd like to learn more about logging in Ktor, check out the Logging topic.

Source code

The application.conf file configures the entry point of our application to be com.example.ApplicationKt.module. This corresponds to the Application.module() function in Application.kt, which is an application module:

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args) fun Application.module() { configureRouting() configureTemplating() }

This module, in turn, calls the following extension functions:

  • configureRouting is a function defined in plugins/Routing.kt, which currently doesn't do anything:

    fun Application.configureRouting() { routing { } }
  • configureTemplating is a function defined in plugins/Templating.kt, which installs and configures the FreeMarker plugin:

    fun Application.configureTemplating() { install(FreeMarker) { templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates") } }

    We'll show how to use FreeMarker templates in the next tutorial: Creating an interactive website.

Static files and pages

Before we dive into making a dynamic application, let's start by doing something a bit easier, but probably just as important – let's get Ktor to serve some static files. In the context of our journal, there are a number of things that we probably want to serve as static files – one example being a header image (a logo that identifies our site).

  1. Create the files directory inside src/main/resources.

  2. Download the ktor_logo.png image file and add it to the created files folder.

  3. To serve static content, we can use a specific routing function already built into Ktor named staticResources. The function takes two parameters: the route under which the static content should be made available, and a lambda where we can define the location from where the content should be served.

    In the plugins/Routing.kt file, let's change the implementation for Application.configureRouting() to look like this:

    import io.ktor.server.application.* import io.ktor.server.http.content.* import io.ktor.server.routing.* fun Application.configureRouting() { routing { staticResources("/static", "files") } }

    This instructs Ktor that everything under the URL /static should be served using the files directory inside resources.

Run the application

Let's see if our application is performing as expected. We can run our application by pressing the Run button next to fun main(...) in our Application.kt:

Run Server

IntelliJ IDEA will start the application, and after a few seconds, we should see the confirmation that the app is running:

[main] INFO Application - Responding at

Let's open http://localhost:8080/static/ktor_logo.png in a browser. We see that Ktor serves the static file:

Ktor logo in browser

Add HTML page

Of course, we are not limited to images – HTML files, or CSS and JavaScript would work just as well. We can take advantage of this fact to add a small 'About me' page as the first real part of our journal application – a static page that can contain some information about us, this project, or whatever else we might fancy.

To do so, let's create a new file inside src/main/resources/files/ called aboutme.html, and fill it with the following contents:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Kotlin Journal</title> </head> <body style="text-align: center; font-family: sans-serif"> <img src="/static/ktor_logo.png" alt="ktor logo"> <h1>About me</h1> <p>Welcome to my static page!</p> <p>Feel free to take a look around.</p> <p>Or go to the <a href="/">main page</a>.</p> </body> </html>

If we re-run the application and navigate to http://localhost:8080/static/aboutme.html, we can see our first page in all its glory. As you can see, we can even reference other static files – like ktor.png – inside this HTML.


Of course, we could also organize our files in subdirectories inside files; Ktor will automatically take care of mapping these paths to the correct URLs.

However, a static page that contains a few paragraphs can hardly be called a journal yet. Let's move on and learn about how templates can help us in writing pages that contain dynamic content, and how to control them from within our application: Creating an interactive website.

Last modified: 13 November 2023