| checksums.txt | 896 B | |
| quilldrop-darwin-amd64 | 13.2 MB | |
| quilldrop-darwin-arm64 | 12.4 MB | |
| quilldrop-linux-386 | 12.3 MB | |
| quilldrop-linux-amd64 | 12.9 MB | |
| quilldrop-linux-arm64 | 12.1 MB | |
| quilldrop-linux-armv6 | 12.3 MB | |
| quilldrop-linux-armv7 | 12.3 MB | |
| quilldrop-windows-386.exe | 12.6 MB | |
| quilldrop-windows-amd64.exe | 13.2 MB | |
| quilldrop-windows-arm64.exe | 12.2 MB |
QuillDrop
QuillDrop is a modern, minimalist blog CMS written in Go. It combines the speed of a static site generator with the flexibility of a dynamic HTTP server — no external database, no JavaScript frameworks, no overhead.
Philosophy
Write. Save. Published.
QuillDrop follows the principle of maximum simplicity: write Markdown files, save them — done. No build tool chaos, no Node.js, no database. A single Go binary handles everything.
Features
Dual-Mode Operation
QuillDrop supports two modes of operation in a single binary:
quilldrop serve— Starts a dynamic HTTP server for local development and preview. Ideal for writing and instantly testing new posts.quilldrop generate— Generates a complete static website as HTML files. Perfect for deployment on Nginx, Apache, CDN, or GitHub Pages.
Markdown with YAML Frontmatter
Posts and pages are written as simple Markdown files with YAML frontmatter:
---
title: "My New Blog Post"
date: 2025-11-06 12:00:00
author: "Jane Doe"
cover: "/images/posts/2025/11/cover.webp"
tags: [Kubernetes, DevOps, Self-Hosted]
categories: [Tech]
preview: "A short preview of the post..."
draft: false
toc: true
---
# Post starts here
Regular Markdown with all the extras...
Supported frontmatter fields:
| Field | Description |
|---|---|
title | Post title |
date | Publication date (multiple formats supported) |
update | Last updated |
author | Post author |
cover / featureImage | Cover image (with fallback) |
tags | List of tags |
categories | List of categories |
preview | Custom preview (otherwise auto-generated from first paragraph) |
draft | Draft — will not be published |
toc | Automatically generate table of contents |
hide | Hide post |
top | Pin post to top |
Extended Markdown Rendering
QuillDrop uses Goldmark as its Markdown engine with the following extensions:
- GitHub Flavored Markdown (GFM) — Tables, strikethrough, autolinks, task lists
- Syntax Highlighting — Over 200 programming languages with the Dracula theme via Chroma
- Emoji Support — Shortcodes like
:rocket:,:tada:,:satellite: - Automatic Heading IDs — For anchor linking and table of contents
- Raw HTML — Embed HTML directly in Markdown
- Hugo Compatibility —
{{</* rawhtml */>}}shortcodes are processed automatically
Responsive Design with Dark/Light Theme
The included theme offers:
- Dark Mode as default with a light alternative theme
- Theme Toggle with localStorage persistence (survives page reloads)
- Futuristic Design — Dark backgrounds, cyan accents, subtle glow effects
- Responsive Layout — Mobile-first, optimized for all screen sizes
- Hamburger Navigation on mobile devices with fullscreen overlay and dedicated stacking context
- Dropdown Menus for nested navigation (touch-optimized on mobile)
- Integrated Search — Magnifying glass in the navbar with Ctrl+K shortcut
- Typography — Inter as body font, JetBrains Mono for code and metadata
Navigation and Menu
The navigation menu is fully configured via config.yaml and supports nested dropdown menus:
menu:
- label: "Home"
url: "/"
- label: "Projects"
children:
- label: "VM-Manager"
url: "/sites/projects/vm-manager"
- label: "VM-Tracker"
url: "/sites/projects/vm-tracker"
- label: "QuillDrop"
url: "/sites/projects/quilldrop"
- label: "About"
url: "/sites/about"
- label: "Tags"
url: "/tags"
New menu items and submenus can be added at any time by simply extending the YAML configuration.
Pagination
The homepage displays a configurable number of posts per page (default: 5). Pagination features:
- Smart page numbering — Shows first and last page, plus a window around the current page
- Ellipsis for many pages (1 ... 10 11 12 13 14 ... 23)
- Newer/Older buttons for quick navigation
- Pretty URLs —
/page/2,/page/3, etc. - SEO-friendly:
/page/1automatically redirects to/(301)
Tags and Categories
QuillDrop supports both tags and categories for content organization:
- Tag overview at
/tags/with post count per tag - Tag pages at
/tags/kubernetes/with all posts for a tag - Category overview at
/categories/with post count per category - Category pages at
/categories/tech/with all posts in a category - Tag and category badges on post cards and detail pages
- Tags and categories are read from YAML frontmatter (
tags,categories)
Full-Text Search
QuillDrop includes an integrated client-side search that works entirely without a backend:
- Search index — A
search-index.jsonwith all posts is generated during build - Lazy loading — The search index is only loaded when the search is first opened
- Multi-term search — Multiple search terms are combined with AND
- Fields — Searches title, preview, tags, and categories
- Keyboard shortcut —
Ctrl+K/Cmd+Kopens the search - Magnifying glass in the navbar — Click the search icon to open the search field
- Debounce — Search results appear after 200ms typing delay
- Maximum 8 results with highlighting of search terms
- Escape or click outside closes the search
- No external service, no framework — pure vanilla JavaScript
Article Navigation
At the end of each blog post, navigation to the previous and next article is displayed:
- Newer article (← left) — Links to the chronologically newer post
- Older article (→ right) — Links to the chronologically older post
- On the newest article, only "Older article" is shown
- On the oldest article, only "Newer article" is shown
- Displays the title of the linked article
Table of Contents
Posts can activate an automatically generated table of contents:
- Enabled via
toc: truein the frontmatter - Supports H1, H2, and H3 headings
- Relative indentation — The TOC detects the minimum heading level and indents relative to it
- Automatic anchor links to the respective headings
- Generated client-side for fast page load times
Static Pages
In addition to blog posts, QuillDrop supports static pages for:
- Legal notice, privacy policy
- About me / About
- Project pages (with subpages)
- Any additional pages
Pages are stored as Markdown files in the sites/ directory. Nested directories are automatically recognized — e.g., sites/projects/vm-tracker/index.md becomes accessible at /sites/projects/vm-tracker.
RSS Feed
Automatically generated RSS 2.0 feed at /index.xml with:
- The latest 20 posts
- Title, link, preview, and publication date
- RSS autodiscovery in the HTML head
- RSS icon in the navigation
- URL
/index.xmlfor compatibility with existing blog setups
Sitemap
Alongside the RSS feed, a sitemap.xml following the sitemaps.org protocol 0.9 is produced — served at /sitemap.xml by the dynamic server and written by the static generator. It contains:
- Homepage and every pagination page (
/page/2/, ...) - All blog posts (
lastmodfromupdate, falling back todate) - All static pages from
sites/ - Tag and category overviews plus every single tag/category page
The sitemap can be switched on and off in the config (default: on):
sitemap:
enabled: true # false disables /sitemap.xml entirely
robots.txt
A robots.txt is generated as well and served at /robots.txt. It references the sitemap automatically whenever sitemap generation is enabled, and is configurable (default: on):
robots:
enabled: true
userAgent: "*" # optional, defaults to "*"
disallow: # optional
- /search-index.json
allow: # optional
- /
Without custom allow/disallow rules an Allow: / is written, i.e. the whole site is crawlable:
User-agent: *
Allow: /
Sitemap: https://blog.kuepper.nrw/sitemap.xml
If sitemap.enabled: false or baseURL is empty, the Sitemap: line is omitted.
Cover Images
Posts can define a cover image that is displayed both on the homepage (as a post card) and on the detail view:
- 21:9 aspect ratio on post cards with zoom-on-hover effect
- Full width on the single post page
- Lazy loading for optimal performance
- Fallback from
covertofeatureImage
Architecture
Project Structure
quilldrop/
├── main.go # CLI entry point
├── config.yaml # Configuration
├── content/ # Blog posts (Markdown)
│ ├── 2025-11-06-my-post.md
│ └── ...
├── sites/ # Static pages
│ ├── about.md
│ ├── legal.md
│ └── projects/
│ └── my-project/
│ └── index.md
├── static/ # Site assets (icons, images, ...)
│ ├── icons/
│ └── images/
├── themes/ # Themes — one directory per theme
│ └── default/
│ ├── templates/
│ │ ├── base.html # Base layout + navbar + search
│ │ ├── home.html # Homepage + pagination
│ │ ├── post.html # Single post + prev/next navigation
│ │ ├── page.html # Static page
│ │ ├── tags.html # Tag overview
│ │ ├── tag.html # Tag page
│ │ ├── categories.html # Category overview
│ │ └── category.html # Category page
│ └── static/ # Theme assets (merged into /static/)
│ ├── css/style.css
│ └── js/
│ ├── theme.js # Dark/Light toggle + TOC generator
│ └── search.js # Client-side full-text search
├── internal/
│ ├── config/config.go # YAML config loader
│ ├── content/
│ │ ├── post.go # Post struct + FlexTime + Tags/Categories
│ │ ├── parser.go # Markdown + frontmatter parser
│ │ └── page.go # Static pages parser
│ ├── server/server.go # HTTP server
│ ├── generator/
│ │ ├── generator.go # Static site generator
│ │ ├── robots.go # robots.txt generator
│ │ └── search.go # Search index generator (JSON)
│ └── templates/
│ ├── render.go # Template engine + functions (loads the theme)
│ ├── rss.go # RSS feed generator
│ └── sitemap.go # sitemap.xml generator
└── output/ # Generated static files
Technology Stack
| Component | Technology |
|---|---|
| Language | Go (standard library + minimal dependencies) |
| HTTP Server | net/http (Go standard library) |
| Templates | html/template, loaded from themes/<name>/templates/ at runtime |
| Markdown | Goldmark + GFM + Emoji + Chroma |
| Configuration | YAML via gopkg.in/yaml.v3 |
| Syntax Highlighting | Chroma (Dracula theme) |
| Fonts | Inter + JetBrains Mono (Google Fonts) |
| CSS | Vanilla CSS with custom properties |
| JavaScript | Vanilla JS — theme toggle, search, TOC (no framework) |
Dependencies
QuillDrop intentionally has minimal dependencies — no web framework, no CSS framework, no JS framework:
github.com/yuin/goldmark— Markdown parser (CommonMark compliant)github.com/yuin/goldmark-emoji— Emoji shortcodesgithub.com/yuin/goldmark-highlighting/v2— Syntax highlightinggithub.com/alecthomas/chroma/v2— Syntax highlighting enginegopkg.in/yaml.v3— YAML parser
Themes
Templates and their assets live outside the binary, in themes/<name>/. The same
QuillDrop binary can therefore drive any number of websites, each with its own look:
themes/
├── default/
│ ├── templates/ # required: base, home, post, page, tags, tag, categories, category
│ └── static/ # optional: CSS, JS, theme images
└── newdesign/
├── templates/
└── static/
The active theme is selected in config.yaml:
themesDir: "themes" # default
theme: "default" # reads from themes/default/
Or temporarily via flag, e.g. to preview a redesign:
./quilldrop serve -theme newdesign
./quilldrop generate -theme newdesign
Creating a new theme:
cp -r themes/default themes/newdesign # or: make new-theme NAME=newdesign
# edit themes/newdesign/templates/*.html and themes/newdesign/static/css/style.css
./quilldrop serve -theme newdesign -dev
Live reload: With -dev the templates are re-read from the theme directory on
every request — changes to HTML, CSS and JS become visible after a browser reload,
without restarting the server (dev mode also sends Cache-Control: no-store so the
browser does not cache assets). A broken template returns a 500 instead of a
half-rendered page; the exact error with file and line is written to the server log.
Without -dev templates are parsed once at startup, as before. New or changed
Markdown files still require a restart.
A theme must provide all eight templates (base.html, home.html, post.html,
page.html, tags.html, tag.html, categories.html, category.html);
QuillDrop aborts with a clear error if one is missing or the theme does not exist.
Static assets are merged: themes/<name>/static/ is copied first, then the
site's own static/ on top — so site files (e.g. static/icons/favicon.ico)
override theme files of the same name. Design belongs to the theme (CSS, JS),
site-specific material stays in static/ (icons, images). Both are served under
/static/; static/images/ and static/videos/ are additionally published at
/images/ and /videos/.
Configuration
All configuration is done via a single config.yaml:
title: "My Blog"
description: "Tech Blog - DevOps, Kubernetes, Self-Hosted"
author: "Jane Doe"
baseURL: "https://my-blog.com"
port: 8080
postsPerPage: 5
contentDir: "content"
sitesDir: "sites"
staticDir: "static"
outputDir: "output"
themesDir: "themes"
theme: "default"
menu:
- label: "Home"
url: "/"
- label: "Tags"
url: "/tags"
- label: "About"
url: "/sites/about"
| Option | Default | Description |
|---|---|---|
title | — | Website title |
description | — | Description (meta tag + hero) |
author | — | Website author |
baseURL | — | Base URL for RSS, sitemap and absolute links |
port | 8080 | Port for the dynamic server |
postsPerPage | 5 | Number of posts per page |
contentDir | content | Directory for blog posts |
sitesDir | sites | Directory for static pages |
staticDir | static | Directory for site assets |
outputDir | output | Output directory for static generation |
themesDir | themes | Directory containing all themes |
theme | default | Active theme, read from <themesDir>/<theme>/ (override: -theme) |
menu | [] | Navigation menu with optional submenus |
sitemap.enabled | true | Generates/serves /sitemap.xml |
robots.enabled | true | Generates/serves /robots.txt (with sitemap reference) |
robots.userAgent | * | User-agent line in robots.txt |
robots.allow | [] | Additional Allow: rules |
robots.disallow | [] | Additional Disallow: rules |
Quick Start
Installation
# Clone the repository
git clone https://github.com/ruedigerp/quilldrop.git
cd quilldrop
# Download dependencies
go mod download
# Build the binary
go build -o quilldrop .
Create a New Post
Create a new Markdown file in the content/ directory:
touch content/2025-12-01-my-first-post.md
---
title: "My First Post"
date: 2025-12-01 10:00:00
author: "Jane Doe"
tags: [Blog, QuillDrop]
preview: "This is my first post with QuillDrop!"
toc: false
---
# Welcome
This is my first post with **QuillDrop**.
Local Preview
# Start the dynamic server
./quilldrop serve
# Or run directly with Go
go run . serve
# With template live reload (theme development)
./quilldrop serve -dev
Then open in the browser: http://localhost:8080
Generate Static Site
# Generate HTML files
./quilldrop generate
# Generated files are in output/
ls output/
The generated files in the output/ directory can be deployed directly to a web server (Nginx, Apache, Caddy) or CDN.
URL Schema
All URLs consistently use trailing slashes to avoid server-side redirects:
| URL | Description |
|---|---|
/ | Homepage (latest N posts) |
/page/2/ | Page 2 of the post list |
/posts/2025-11-06-my-post/ | Single blog post |
/tags/ | Tag overview |
/tags/kubernetes/ | Posts with tag "Kubernetes" |
/categories/ | Category overview |
/categories/tech/ | Posts in category "Tech" |
/sites/about/ | Static page |
/sites/projects/vm-tracker/ | Nested project page |
/index.xml | RSS feed |
/sitemap.xml | Sitemap for search engines (if enabled) |
/robots.txt | Crawler rules + sitemap reference (if enabled) |
/search-index.json | Search index (JSON) |
/static/css/style.css | Static assets |
/images/posts/2025/11/cover.webp | Images |
Why QuillDrop?
- No database — The filesystem is the only data source
- No build pipeline — A single
go buildand you're done - No JS frameworks — Vanilla JavaScript for theme, search, and TOC
- Minimal dependencies — 5 Go packages, all focused on Markdown
- Blazing fast — Generates 100+ posts in under 3 seconds
- Single binary — Templates embedded, no runtime setup needed
- Hugo compatible — Existing Hugo posts with frontmatter just work
- Dual-mode — Development with server, production with static generator
License
QuillDrop is open source.