← All posts

How we store docs as Markdown in Git

docs-as-codemarkdowngitarchitectureself-hosted

“Store your docs as Markdown in Git” is easy to say and easy to get wrong. Plenty of tools export to Markdown, or sync a copy to a repo while the real content lives in their database. That’s not the same thing. Here’s how DocuCommit does it for real — where the files live, how versioning works, and how you still get the features a database usually provides, without one.

Every page is a file

A DocuCommit page is a plain .md file on disk, with a little YAML frontmatter at the top for metadata:

---
title: Authentication
slug: auth
tags:
  - security
  - jwt
---

# How tokens are signed

We use HS256 with a rotating secret pulled from [Vault](/infra/vault).

That’s the whole storage format. The file is the source of truth — not a cache, not a mirror. Folders become sections; the frontmatter carries the title, slug, and tags. You can open the repo in any editor, grep it, or run a script over it, and you’re touching exactly what the app reads and writes. There is no serialization step between “your content” and “a file you own.”

Versioning is just Git

Because pages are files in a repository, version history isn’t a feature we had to build and store somewhere — it’s native Git history. Every published change is a commit with an author, a message, a timestamp, and a real diff. Browsing revisions is reading the log; restoring an old version is a checkout. Blame and the full change graph come for free, and they live in your repo, forever, with no retention window.

This is the part that’s hard to bolt on later. A database-backed tool has to model revisions as rows and decide how long to keep them. With Git, the history model is the storage model. You don’t administer it; you already know how it works.

Two surfaces over one repo

DocuCommit is two programs pointed at the same files:

  • The editor (a desktop app) is where writing happens. It reads the Markdown, gives you a WYSIWYG surface, and on save writes the file back and — when you choose — commits, pulls, and pushes. Git actions are user-initiated; nothing syncs behind your back.
  • The server is a read-only, self-hosted site for readers. It only ever pulls and renders — it never writes. Point it at the repo (or a mirror) and it publishes the current state to your team.

One repository, two lenses. The editor owns writes; the server owns reads. Neither needs a database to coordinate, because the repo is the coordination point. The exact on-disk shape is documented in Repository layout, and the general model — including when it’s the wrong choice — in Git-backed documentation, explained.

”But where do search, comments, and diagrams come from?”

This is the usual objection to flat files, and it’s fair. The answer is that a file is where content lives — it says nothing about what you compute on top of it:

  • Search is a full-text index (Lucene) rebuilt from the files. The index is derived state: throw it away and regenerate it any time. Your content never depends on it.
  • Comments live in a sidecar on the server, outside the repository — never inside the Markdown. Your prose stays clean and reviewable, and the discussion never smears through the content.
  • Diagrams are authored with bundled draw.io and fenced Mermaid. Mermaid is text that diffs cleanly; a draw.io diagram is a .drawio + SVG pair that versions with the page, though it does not review as a readable diff.
  • Rendering goes through a single Markdown↔HTML authority (flexmark), so what you write, what the editor shows, and what the server publishes all agree.

Every one of these is derived from the files or stored beside them. None of them is a reason to put your knowledge in a database.

Why this shape matters

The payoff of “docs as files in Git” isn’t purity for its own sake — it’s what it removes:

  • No lock-in. There’s nothing to export because nothing was ever captured. The files are yours the whole time.
  • No separate history to trust. Your change log is Git’s log, in your repo.
  • No black box. You can read, script, back up, and reason about your entire knowledge base with tools you already have.

And, almost as a side effect: because it’s all plain Markdown, every page is readable by any LLM or agent with no export step — but that’s a whole other post.

See it work

The fastest way to believe “the file is the source of truth” is to watch a save land as a file, and a snapshot land as a real commit. There’s a live demo, no signup:

Browse the live demo

Or run it on your own infrastructure with the 14-day trial — no credit card, and your docs are plain Markdown in your Git from the first save.

See it for yourself

Docs as Markdown in your Git.

No database, no lock-in. Browse the live demo with no signup, or start a 14-day trial on your own infrastructure — no credit card required.