bx-Blogger: A Markdown-First Blog Engine Built for Developers

By Michael Rigsby · · 8 min read

A blog platform for developers, built with markdown-first editing, syntax-highlighted code blocks, and auto-detected embeds. It offers a fast, extendable admin interface, customizable themes, and built-in SEO features.

If you've ever set up WordPress just to publish a few technical posts and ended up babysitting plugin updates instead of writing, you already understand the gap bx-Blogger is trying to fill. This is the platform I wanted: simple to deploy, comfortable to edit in, and built around the format I actually write in every day. That format is markdown.

The pitch in one paragraph

bx-Blogger is a self-hosted blog engine for developers. Posts are markdown. Pages are markdown. Code blocks are syntax-highlighted out of the box. Embeds (YouTube, Vimeo, Spotify, CodePen, Twitter/X) are auto-detected from a bare URL on its own line. The admin runs in your browser; everything ships in a Docker image you can spin up in a few minutes; and the whole thing is built on BoxLang and ColdBox 8, which means it's fast, easy to extend, and doesn't drag a Node-runtime supply chain around with it.

Admin Dashboard

Why markdown-first matters

There's a particular kind of friction that comes from writing in a WYSIWYG editor when your brain works in fenced code blocks and bullet lists. You get a third of the way through a post about, say, async patterns in .NET, and suddenly you're fighting the editor about indentation in a code sample, or pasting a syntax-highlighted snippet that looks great until you switch themes.

bx-Blogger sidesteps that entirely. Every post body is a markdown textarea, backed by EasyMDE for an inline toolbar, syntax-aware preview, and side-by-side mode. You write the same markdown you'd write in your README. The renderer takes care of code highlighting, embed expansion, image responsive sizing, and the rest.

Admin easyMDE Side-by-Side

What ships in the box

A non-exhaustive tour of what you get the first time you docker compose up:

Posts and pages

Posts have everything you'd expect: titles, slugs, excerpts, featured images, categories (hierarchical), tags, author bylines, scheduled publishing, draft / published / scheduled / trashed lifecycle, and a per-post visibility toggle (public, private, password-protected). Pages share the same machinery with a post_type='page' switch. They live at the root URL, so /about is just a page with the slug about.

Pinned-to-home featured posts are a single checkbox on the post editor. The home page rolls them into a dedicated section above the post grid when the active theme has the option turned on.

Three themes, all serious

bx-Blogger ships with three themes you can use as-is or fork:

  • Default: clean, responsive, Bootstrap 5.3 with a custom Vite-built SCSS overlay. The everyday workhorse.
  • Magazine: warmer palette, serif headings, a hero-card home layout. Proves the theme seam by being visually distinct from default while running on the same option set.
  • Fallback: vanilla Bootstrap with no build step. Always installed, never uninstallable. Activates automatically if your configured theme can't be resolved on disk, so a botched theme swap can't take the public site down.

Every shipped theme exposes the same set of operator-editable options:

  • Logos (light + dark variants), favicon, site title, hide-title toggle
  • Accent colors (light + dark) that cascade through buttons, links, focus rings, card borders, and tag pills via Bootstrap's --bs-primary custom property
  • Intro page slots for /, /blog, /tags, /categories, /search, and a custom 404
  • Post-card display toggles (excerpt, author, date, reading time)
  • Featured-post section opt-in with configurable count
  • Custom CSS injection (cascade-priority, no !important needed)
  • Custom <head> HTML for analytics and verification snippets
  • Footer brand toggle, custom footer text, RSS link visibility

Adding your own theme is a folder drop. A theme is a theme.json manifest, a layouts/ folder (Main, Archive, Page, Post), a views/ folder (home, blog, tags, categories, etc.), a partials/ folder, and (optionally) a src/ folder with SCSS and JS that the included Vite build picks up automatically.

Media library

A first-class media library handles uploads, alt text, captions, automatic responsive variants (theme-declared thumbnail sizes get generated on upload), and a search-as-you-type picker that surfaces inside the post editor's image button and inside theme options for logo / favicon picks.

Storage is pluggable. Out of the box you can run with local disk (handy for development) or any S3-compatible provider. The default examples in the docs target Backblaze B2, which has cheaper egress than AWS S3 for the same API and no real lock-in.

Built-in SEO and discoverability

  • Auto-generated sitemap.xml covering posts, pages, tag pages, category pages, and authors
  • Per-post Open Graph image generation as a background job; images are produced server-side from a template, no third-party rendering service required
  • RSS feed at /feed.xml with autodiscovery <link> in every theme's <head>
  • SEO description override per post (defaults to excerpt)
  • Custom 404 that you can swap for any published page

A real search endpoint at /search that hits the published post index, with no third-party JavaScript bundle, no API key, and no "search-as-a-service" subscription. Theme-controlled intro page above the results so you can put guidance ("try a category instead of a keyword") in front of users who land here from a 404.

Permissions and multi-author

Roles ship with sensible defaults (super_admin, admin, editor, author), and the permissions matrix is surfaced in the admin so an operator can see exactly who can do what without grepping through code. New permissions register themselves; you don't have to remember to add them to a settings table.

A user with a public author profile gets their own page at /author/{slug} and shows up in the public /authors directory.

Page cache that doesn't betray you

Public pages are aggressively cached, but cache invalidation is wired to the right events: publishing a post wipes the listing pages it would appear on; updating a theme option flushes the entire public surface (because options drive rendered HTML); editing a tag or category invalidates the corresponding archive. You don't think about it.

AI you actually own

Optional, off by default, BYO-key. When you turn it on (one .env block) you get:

  • Generate draft from idea: describe a post in a sentence, get a markdown draft you can edit
  • Review & suggest edits: ask the model to review the current body and return structured suggestions (style, clarity, grammar, structure) that surface inline in the editor for accept / reject
  • Generate image: produce a featured or inline image from a prompt; pick from multiple variations; insert at the cursor

bx-Blogger doesn't lock you into a single AI vendor. Every AI feature runs through bx-ai, the BoxLang module that abstracts the provider, so you can point at OpenAI, Anthropic, OpenRouter, a local Ollama instance, or anything else bx-ai supports without touching application code. Text and image generation each have their own model knob (cheap-fast text alongside nicer-quality image, or whatever combination suits you). Per-month token and image budgets keep surprise bills off the table. There's a mock provider for tests so the whole flow runs without an API key in CI.

Every system prompt powering the AI features is editable from the admin under System Settings → AI Prompts. Each prompt has its own active / inactive toggle: when inactive, the in-code default takes over, so a bad edit can't break the editor. "Reset to default" is one click. The security guardrails (the jailbreak-protection block appended after the operator's prompt) stay hard-coded so a compromised admin account can't turn them off.

Redirects

If you're migrating off another platform, the redirects manager lets you stand up /old-slug → /new-slug rules without touching the web server config. Wildcard support, hit counters, last-used timestamps. A small thing that saves a bigger headache.

Stack and ops

Under the hood:

  • BoxLang running on the Ortus CommandBox base Docker image
  • ColdBox 8 as the MVC framework, with HMVC modules for the admin
  • CBWIRE for the reactive admin surface (Livewire-style server-driven components, with no SPA and no frontend build pipeline for the admin)
  • MySQL 8.4 for the primary store
  • Quick as the ORM, qb for query building
  • cbq for background jobs (OG card rendering, scheduled publishing, AI requests)
  • Vite for theme asset compilation (only needed when you customize a theme)
  • MailHog in dev for outbound email capture; production uses whatever SMTP you configure

The whole thing is intentionally Docker-first. Production is a docker compose up away. Migrations apply on container start. Background workers run in a separate worker container so a slow job can't tie up a request thread.

There's no Java to install, no PHP-FPM to tune, no Node runtime to maintain. If you can run Docker, you can run bx-Blogger.

Self-hosting honesty

This is a self-hosted platform. That comes with the usual asterisks:

  • You're responsible for backups (the docs include a sample script)
  • You're responsible for TLS (the deployment notes assume a reverse proxy like Caddy or Traefik in front)
  • You're responsible for security updates to the base image (CI rebuilds the image on a schedule so you get fresh patches when you redeploy)

In exchange you get: no per-seat pricing, no "you used too many posts" letters, no surprise UI redesign because someone in product wanted to ship a feature, no analytics-by-default, no tracking pixels, no growth-hacking newsletter signup popovers.

What's next

bx-Blogger is in active development. The roadmap (in priority order) for the next few releases includes:

  • A first-party newsletter integration that doesn't phone home to a third-party
  • Webmentions support (so other people's links to you can populate a "mentioned by" section under each post)
  • A Plausible / Umami-style aggregated stats panel inside the admin (no per-visitor tracking, just totals)

If any of that sounds useful, follow the tags for bx-blogger and release-notes. I'll be writing about each of these features as they ship: what the design constraints were, what tradeoffs got made, and how to use them once they're out.

I'd love to hear what you think.