How to Build a Markdown-Powered Blog with Kotlin and HTTP4k
How to Build a Markdown-Powered Blog with Kotlin and HTTP4k
You can build a fully functional blog engine in Kotlin with no database, no CMS, and no build step for content. This post walks through how we built the Outerstellar blog using fragments4k and HTTP4k.
Why Markdown for a Kotlin blog?
Markdown files are portable, version-controllable, and readable without tooling. Combined with YAML front matter, they provide enough structure for a blog without the overhead of a database or CMS. fragments4k reads these files at startup and provides everything a blog needs: routing, pagination, RSS, sitemaps, and full-text search.
What you need
- JDK 21+
- Maven or Gradle
- A text editor for writing Markdown
Step 1: Add the dependency
<dependency>
<groupId>io.github.rygel</groupId>
<artifactId>fragments-http4k</artifactId>
<version>0.6.3</version>
</dependency>
This pulls in the HTTP4k adapter, which includes the core library, blog engine, RSS generation, sitemap, and Lucene search.
Step 2: Create content
Create a directory content/blog/ and add a Markdown file:
---
title: My First Post
slug: my-first-post
date: 2026-03-31
tags: [kotlin, web]
template: blog
visible: true
author: Your Name
preview: A short summary that appears in listings and meta descriptions.
---
# My First Post
Your content here. Use standard Markdown — headings, code blocks, tables, links.
A `<!--more-->` tag separates the preview from the full content.
Step 3: Wire up the application
val repository = FileSystemFragmentRepository("./content/blog", baseUrl = "/blog")
val blogEngine = BlogEngine(repository, pageSize = 10)
val searchEngine = LuceneSearchEngine(listOf(repository), null)
searchEngine.index()
val adapter = FragmentsHttp4kAdapter(
blogEngine = blogEngine,
renderer = JTETemplates().HotReload("src/main/jte"),
searchEngine = searchEngine,
siteTitle = "My Blog",
siteDescription = "A technical blog about Kotlin",
siteUrl = "https://example.com",
)
val app = adapter.createRoutes()
app.asServer(Jetty(8080)).start()
This gives you:
| Route | What it does |
|---|---|
/blog | Paginated post listing |
/blog/2026/03/my-first-post | Individual post |
/blog/tag/kotlin | Posts filtered by tag |
/rss.xml | RSS feed |
/sitemap.xml | XML sitemap |
/search?q=kotlin | Full-text search |
/robots.txt | Crawler guidance |
/llms.txt | AI crawler summary |
Step 4: Add templates
fragments4k passes view models to your template engine. With JTE (Kotlin templates):
@param model: ContentViewModel
<article>
<h1>${model.viewModel.title}</h1>
<time>${model.viewModel.date}</time>
<div class="prose">
$unsafe{model.viewModel.content}
</div>
</article>
The ContentViewModel provides title, date, author, tags, reading time, previous/next post links, and the rendered HTML content.
Step 5: Deploy
Build a fat JAR with Maven Shade and run it anywhere:
mvn clean package
java -jar target/my-blog.jar
No database migrations, no CMS updates, no build pipeline for content. Add a new post by creating a Markdown file and restarting the application (or use dev mode for hot-reloading).
FAQ
Can I build a blog in Kotlin without a database?
Yes. fragments4k reads Markdown files from the filesystem and provides routing, pagination, RSS, sitemaps, and full-text search via an embedded Lucene index. No database is required.
What is fragments4k?
fragments4k is a Markdown-based content engine for Kotlin that runs inside your JVM application as a library. It parses .md files with YAML front matter and provides blog listings, tag filtering, search, and RSS feeds. Adapters exist for HTTP4k, Spring Boot, Javalin, Quarkus, and Micronaut.
How does fragments4k compare to Hugo or Jekyll?
Hugo and Jekyll are static site generators that produce HTML files at build time. fragments4k runs at runtime inside your JVM application, which means you get live search, dynamic filtering, and hot-reloading without a rebuild step. The tradeoff is that you need a running JVM process instead of static files.