You know Kotlin. You build Android apps, maybe KMP apps. But what about the backend?

Every app needs a server. An API to fetch data from. A backend to store users, handle payments, send notifications.

You could learn Node.js and Express. Or Python and FastAPI. Or Java and Spring Boot.

Or you could use the Kotlin you already know — and build your backend with Ktor.

What is Ktor?

Ktor is a backend framework built by JetBrains — the same company that created Kotlin. It lets you build web servers, REST APIs, microservices, and web applications using Kotlin.

The key idea: Ktor is Kotlin-native, lightweight, and coroutine-based.

┌─────────────────────────────────────────┐
              Your Ktor Server           
                                         
  Routes, authentication, database,      
  file uploads, WebSockets, caching      
└────────┬──────────┬──────────┬──────────┘
                             
    ┌────▼────┐ ┌───▼────┐ ┌──▼──────┐
     Android    iOS       Web   
       App      App       App   
    └─────────┘ └────────┘ └─────────┘

Instead of learning a new language for backend, you use the same Kotlin you already know. Same syntax. Same coroutines. Same data classes.

Why Ktor?

There are many backend frameworks. Here is why Ktor stands out for Kotlin developers:

1. Built for Kotlin

Ktor is not a Java framework with Kotlin support. It was built from the ground up in Kotlin. Everything uses Kotlin features — coroutines, extension functions, DSL builders, data classes.

// This is a complete Ktor server
fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/hello") {
                call.respondText("Hello from Ktor!")
            }
        }
    }.start(wait = true)
}

No annotations. No XML configuration. No magic. Just Kotlin code.

2. Coroutine-Based

Every request handler in Ktor is a coroutine. This means you can use suspend functions, Flow, and structured concurrency. No callback hell. No thread management.

get("/users/{id}") {
    val id = call.parameters["id"]?.toIntOrNull()
    // This is a suspend function - no blocking
    val user = userRepository.findById(id)
    call.respond(user)
}

3. Lightweight and Modular

Ktor does not include everything by default. You add only what you need through plugins:

  • Need JSON? Add the serialization plugin.
  • Need authentication? Add the auth plugin.
  • Need a database? Add Exposed.
  • Need WebSockets? Add the WebSocket plugin.

Your server only contains what you actually use. No bloat. No unused features.

4. JetBrains-Backed

Ktor is maintained by JetBrains, the company behind Kotlin, IntelliJ IDEA, and Android Studio. It has dedicated engineers, regular releases, and long-term support.

Ktor vs Other Frameworks

Here is how Ktor compares to popular backend frameworks:

KtorSpring BootExpress.jsFastAPI
LanguageKotlinJava/KotlinJavaScriptPython
ApproachLightweight, modularFull-featured, batteries-includedMinimal, middleware-basedModern, async
ConfigurationCode DSLAnnotations + YAMLMiddleware chainDecorators
Async modelCoroutinesReactive/Virtual ThreadsEvent loopasync/await
Learning curveEasy (if you know Kotlin)SteepEasyEasy
Startup timeFast (~1-2 seconds)Slow (~5-15 seconds)FastFast
Memory usageLow (~50 MB)High (~200+ MB)LowLow
Best forKotlin developers, microservicesEnterprise, large teamsFull-stack JSData science, APIs

When to Choose Ktor

  • You already know Kotlin
  • You want a lightweight server (microservices, APIs)
  • You prefer code over annotations and XML
  • You want fast startup and low memory usage
  • You are building a backend for your mobile app

When to Choose Something Else

  • Your team only knows Java → Spring Boot
  • You need an enterprise framework with everything built-in → Spring Boot
  • You are a full-stack JavaScript developer → Express.js or Next.js
  • You are building a data science API → FastAPI

Who Uses Ktor?

Ktor is used by companies of all sizes:

  • JetBrains — Uses Ktor internally for many services
  • Amazon — Kotlin backend services
  • Netflix — Microservices
  • DoorDash — API services
  • Expedia — Travel platform services

The Kotlin ecosystem is growing fast. In the 2025 JetBrains developer survey, 50% of Kotlin developers use it for backend development. Ktor adoption grew 37% in one year.

Your First Ktor Server

Let’s build a simple server. You need:

  • JDK 21 or later
  • IntelliJ IDEA (Community or Ultimate)
  • Gradle

Step 1: Create the Project

Create a new Gradle project with this build.gradle.kts:

plugins {
    kotlin("jvm") version "2.3.0"
    application
}

group = "com.kemalcodes"
version = "0.0.1"

application {
    mainClass.set("com.kemalcodes.ApplicationKt")
}

repositories {
    mavenCentral()
}

val ktorVersion = "3.1.2"

dependencies {
    implementation("io.ktor:ktor-server-core:$ktorVersion")
    implementation("io.ktor:ktor-server-netty:$ktorVersion")
    implementation("ch.qos.logback:logback-classic:1.5.18")

    testImplementation("io.ktor:ktor-server-test-host:$ktorVersion")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
    testImplementation("org.junit.jupiter:junit-jupiter:5.12.2")
}

tasks.test {
    useJUnitPlatform()
}

kotlin {
    jvmToolchain(21)
}

Step 2: Write the Server

Create src/main/kotlin/com/kemalcodes/Application.kt:

package com.kemalcodes

import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun main() {
    embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
        module()
    }.start(wait = true)
}

fun Application.module() {
    routing {
        get("/") {
            call.respondText("Hello, Ktor!")
        }
    }
}

Let’s break this down:

  • embeddedServer(Netty, port = 8080) — Creates a server using the Netty engine on port 8080
  • module() — Configures the application with plugins and routes
  • routing { } — Defines URL routes
  • get("/") — Handles GET requests to the root path
  • call.respondText() — Sends a text response

Step 3: Add Logging

Create src/main/resources/logback.xml:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

Step 4: Run the Server

./gradlew run

Open your browser and go to http://localhost:8080. You should see:

Hello, Ktor!

That’s it. Your first Ktor server is running.

Step 5: Write a Test

Create src/test/kotlin/com/kemalcodes/ApplicationTest.kt:

package com.kemalcodes

import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.testing.*
import kotlin.test.Test
import kotlin.test.assertEquals

class ApplicationTest {

    @Test
    fun `root endpoint returns hello`() = testApplication {
        application {
            module()
        }
        val response = client.get("/")
        assertEquals(HttpStatusCode.OK, response.status)
        assertEquals("Hello, Ktor!", response.bodyAsText())
    }
}

Run the test:

./gradlew test

The test creates a virtual server (no real HTTP server starts), sends a request, and checks the response. Fast and reliable.

Key Concepts

Before we dive deeper in the next tutorials, let’s understand the core concepts:

Plugins

Ktor uses a plugin system. Each feature is a separate plugin you install:

fun Application.module() {
    install(ContentNegotiation) {
        json()
    }
    install(Authentication) {
        jwt("auth") { /* config */ }
    }
    routing {
        // routes
    }
}

You only install what you need. This keeps your server lightweight.

Routing

Routes map URLs to handler functions. You can group routes, use path parameters, and organize them into separate files:

routing {
    route("/api") {
        route("/users") {
            get { /* list users */ }
            post { /* create user */ }
            get("/{id}") { /* get user by id */ }
        }
    }
}

Engine

Ktor supports multiple server engines:

  • Netty — Most popular, high performance
  • Jetty — Java ecosystem standard
  • CIO — Coroutine-based, pure Kotlin
  • Tomcat — Traditional Java servlet engine

We will use Netty throughout this series. It is the most popular choice and works great for most use cases.

How Ktor Compares to Ktor Client

If you have been following our KMP Tutorial series, you already know Ktor Client — the HTTP client that runs on Android, iOS, Desktop, and Web.

Ktor Server is the other half of the Ktor project. While the client makes HTTP requests, the server handles them:

┌──────────────┐         ┌──────────────┐
│  Ktor Client │  HTTP   │  Ktor Server │
│  (KMP App)   │ ────→   │  (Your API)  │
└──────────────┘         └──────────────┘

They share concepts like ContentNegotiation, HttpStatusCode, and ContentType. If you know one, the other feels familiar.

Getting Started Options

There are three ways to create a Ktor project:

1. Ktor Project Generator (Web)

Go to start.ktor.io, select your plugins, and download a ZIP file. This is the fastest way to get started.

2. IntelliJ IDEA Plugin

IntelliJ IDEA Ultimate has a built-in Ktor project wizard. Go to File > New > Project > Ktor. This generates the same project as the web generator.

3. Manual Setup (What We Do)

Create a Gradle project yourself and add dependencies manually. This gives you full control and helps you understand every part of the project.

We use the manual approach in this series because it teaches you how everything fits together.

What We Will Build in This Series

Over the next tutorials, we will build a complete backend API. Here is the roadmap:

  1. What is Ktor? — You are here
  2. Ktor vs Spring Boot — Detailed comparison
  3. Project Setup — Application structure and configuration
  4. Routing — Handling HTTP requests
  5. Serialization — JSON with kotlinx.serialization
  6. Database — Exposed ORM with H2
  7. CRUD Operations — Building a REST API
  8. Relationships — Advanced queries with Exposed
  9. File Uploads — Multipart and static files
  10. Migrations — Database versioning with Flyway
  11. Authentication — JWT tokens and password hashing

By the end, you will have a production-ready REST API with authentication, database, file uploads, and proper error handling.

Source Code

You can find the source code for this tutorial on GitHub:

github.com/kemalcodes/ktor-tutorial — Branch: tutorial-01-what-is-ktor

What’s Next?

In the next tutorial, we will compare Ktor with Spring Boot in detail. You will learn when to use each framework and understand the trade-offs.

Ktor Tutorial #2: Ktor vs Spring Boot — Which Kotlin Backend?