Configuration
gomddoc runs with no configuration: point it at a folder and it serves README.md as the home page with the
default theme. This page covers every setting for when you want to change that. The binary carries the same
reference, so gomddoc info always matches the version you run.
Where settings come from
Each setting can come from four places. The highest one wins:
- Command-line flags and the positional directory argument
GOMDDOC_*environment variables.gomddoc/config.ymlin the content directory- Built-in defaults
Settings fall into two groups:
- Site settings (title, theme, search, URLs) can be set in the config file, by environment variable, and in a few cases by flag.
- Server settings (ports, timeouts, dev mode, Basic Auth, Git credentials) are set by flag or environment variable
only. They are never read from
config.yml.
dir_index and dev mode are the two exceptions to "highest wins": a flag can turn them on, but can't turn off a
true set by the file or the environment.
When the content comes from a Git URL, a .gomddoc/config.yml inside the repository is ignored. Configure
Git-sourced sites with environment variables. exclude, strip_extensions and theme.vars have no environment
variable, so those sites run with their defaults.
The config file
.gomddoc/config.yml holds the site settings at the top level, with no site: wrapper. This example sets every key:
meta:
title: My Project # default: the directory name, title-cased
description: Docs for My Project
domain: docs.example.com # bare host: no scheme, no path
robots: "" # site-wide <meta name="robots">, e.g. noindex
default_index: README.md # file served for a directory URL
dir_index: false # generated listing when a directory has no index
language: en-US # BCP 47 language of the root content
edit_url: https://github.com/my-org/my-repo/edit/main/docs
exclude: # glob patterns hidden from every route
- drafts/
- "*.bak"
strip_extensions: [.md] # extensions removed from URLs
theme:
name: default
features: # all default to true
mermaid: false
vars: # emitted as --theme-<key> CSS properties
primary: "#7c3aed"
highlighting:
theme: github # any Chroma style name
search:
index: true # build the full-text search index at startup
Every key is optional. gomddoc init writes a starter file with the common defaults filled in.
The loader is strict. An unknown key, a misplaced nesting level or a site: wrapper is a startup error that names
the line, and so is a second YAML document after a --- separator. An empty or comment-only file means "use the
defaults".
Site settings
| Key | Environment variable | Default |
|---|---|---|
meta.title |
GOMDDOC_SITE_META_TITLE |
Directory name, title-cased |
meta.description |
GOMDDOC_SITE_META_DESCRIPTION |
empty |
meta.domain |
GOMDDOC_SITE_META_DOMAIN |
empty |
meta.robots |
GOMDDOC_SITE_META_ROBOTS |
empty |
default_index |
GOMDDOC_SITE_DEFAULT_INDEX |
README.md |
dir_index |
GOMDDOC_SITE_DIR_INDEX |
false |
language |
GOMDDOC_SITE_LANGUAGE |
en-US |
edit_url |
GOMDDOC_SITE_EDIT_URL |
empty |
theme.name |
GOMDDOC_SITE_THEME_NAME |
default |
theme.features.<k> |
GOMDDOC_SITE_THEME_FEATURES_<K> |
true |
highlighting.theme |
GOMDDOC_SITE_HIGHLIGHTING_THEME |
github |
search.index |
GOMDDOC_SITE_SEARCH_INDEX |
true |
exclude |
none | empty |
strip_extensions |
none | [.md] |
theme.vars |
none | empty |
A few keys need more detail:
meta.domainturns on canonical URLs,og:url,sitemap.xml,feed.xml, JSON-LD and theSitemap:line inrobots.txt. It must be a bare host such asdocs.example.com. The--domainflag overrides it.default_indexcan't be empty.dir_index: falsemakes a directory without an index file redirect to the first page of the navigation, or answer 403 when there is none.truerenders a generated listing instead.edit_urlmust usehttporhttps. The page's file path is appended to it, sohttps://github.com/org/repo/edit/mainlinksguide/setup.mdto.../edit/main/guide/setup.md.search.index: falseskips building the index on large sites./api/searchthen returns 404 and the MCPsearch_docstool reports that search is unavailable. The theme's search button is a separate toggle,theme.features.search.highlighting.themeaccepts any Chroma style name, such asgithub,dracula,monokai,nordorcatppuccin-mocha.
Excluding files
Dotfiles and dot-directories (.git/, .env, .gomddoc/) are never served, except .well-known/. exclude adds
your own patterns:
exclude:
- drafts/ # trailing slash: a directory and everything in it
- "*.bak" # no slash: matched against each path segment
- internal/*.md # with a slash: matched against the full path
- TODO.md
Patterns use Go's path.Match syntax (*, ?, [abc]). An excluded file returns the same 404 as a missing one,
under its clean URL too, and is left out of navigation, search, tags, the sitemap, the feed, MCP and build output.
gomddoc doctor warns about a pattern that matches nothing, which is usually a typo.
Clean URLs
strip_extensions lists the extensions removed from URLs. With the default [.md], guide.md is served at /guide
and /guide.md redirects there with a 301. Only rendered content is affected; images, CSS and JavaScript keep their
extensions.
strip_extensions: [.md, .html] # also serve guide.html at /guide
strip_extensions: [] # keep extensions in URLs
Each entry must start with a dot. When two files claim the same clean URL, such as guide.md and guide.html, the
extension listed first wins and gomddoc doctor reports the collision. In a static build, stripped pages are written
as guide/index.html, so clean URLs work on any static host.
Themes
Choosing a theme
theme:
name: nord
Only default is bundled in the binary. The other seven (academic, gitbook, material, midnight, minimal,
nord and ocean) come from gomddoc-themes: copy one into
.gomddoc/assets/themes/<name>/ before selecting it. A theme that isn't installed falls back to default with a
warning, and gomddoc doctor reports it.
Feature toggles
Features are optional parts of a theme. All default to on. Turn one off for the whole site:
theme:
features:
katex: false
mermaid: false
Or with an environment variable, where the name after FEATURES_ is lowercased to form the key:
GOMDDOC_SITE_THEME_FEATURES_MERMAID=false gomddoc serve ./docs
The default theme reads these twelve:
| Feature | Controls |
|---|---|
search |
Search button and Ctrl+K dialog |
toc |
Table of contents and scroll highlighting |
dark_mode |
Light/dark toggle |
code_copy |
Copy button on code blocks |
katex |
KaTeX math rendering |
mermaid |
Mermaid diagrams |
admonitions |
> [!NOTE] callouts; off, they render as plain quotes |
heading_anchors |
Hover links on headings |
color_chips |
Swatches next to hex codes in backticks |
tag_chips |
Tag links at the top of the page |
see_also |
Related pages at the end of the page |
annotations |
Annotations button and notes on the page content |
Other themes can read other keys. Every downloadable theme except minimal reads google_fonts, which stops it
loading fonts from Google. Run gomddoc info to see the keys the active theme reads. Keys must match
^[a-z][a-z0-9_]*$.
A page can override any feature in its frontmatter, in either direction. Site defaults apply first, then the page's values:
---
features:
katex: true
---
Theme variables
theme.vars becomes a :root block of CSS custom properties, one --theme-<key> per entry:
theme:
vars:
primary: "#2563eb"
dark-primary: "#60a5fa"
The default theme reads primary, bg, text, dark-primary, dark-bg and dark-text. Keys may contain
letters, digits and hyphens. A value containing {, }, ;, < or > is skipped with a warning.
Overriding templates
To change one piece of any theme, put a partial in .gomddoc/partials/. A file there, such as footer.html.tmpl,
replaces the partial of the same name in whichever theme is active, and survives a change of theme.name.
For a full custom theme, create .gomddoc/assets/themes/<name>/layouts/default.html.tmpl. The default theme's
partials stay available, so you only write what you change. Add layouts/error.html.tmpl for error pages, and any
other layouts/<name>.html.tmpl becomes selectable with layout: <name> in frontmatter. Static files in
.gomddoc/static/ are served under /_assets/.
Templates receive the site settings, the rendered page, its frontmatter, TOC, navigation tree, breadcrumbs,
previous and next pages, and related pages. Helper functions include contentURL for internal links, canonicalURL,
assetURL and inlineJSAsset / inlineCSSAsset / inlineHTMLAsset. The full list is in
gomddoc help theming-and-assets.
Server settings
These are set by flag or environment variable only.
| Environment variable | Flag | Default | Purpose |
|---|---|---|---|
GOMDDOC_SERVER_DIR |
positional <dir> |
. |
Content directory or Git URL |
GOMDDOC_SERVER_PORT |
-p, --port |
:8080 |
Listen address; :auto takes the first free port from 8080 |
GOMDDOC_SERVER_ADMIN_PORT |
--admin-port |
empty | Separate listener for metrics, pprof and health |
GOMDDOC_SERVER_PPROF |
--pprof |
false |
Expose /debug/pprof/ |
GOMDDOC_SERVER_BASIC_AUTH_FILE |
--basic-auth-file |
empty | htpasswd file, bcrypt hashes only |
GOMDDOC_SERVER_GIT_SSH_KEY |
--git-key-file |
empty | SSH private key for git+ssh:// sources |
GOMDDOC_SERVER_GIT_STORAGE_DIR |
--git-storage-dir |
empty | Clone to disk instead of memory |
GOMDDOC_SERVER_DEV_MODE |
none | false |
Dev mode for serve; always on in preview |
A bare admin port such as :9090 binds 127.0.0.1. Give a host, such as 0.0.0.0:9090, to listen on other
interfaces. Dev mode turns off template and asset caching, so theme edits show on refresh. There is no --dev flag.
HTTP tuning
| Environment variable | Default | Maximum |
|---|---|---|
GOMDDOC_SERVER_HTTP_READ_HEADER_TIMEOUT |
5s |
1m |
GOMDDOC_SERVER_HTTP_WRITE_TIMEOUT |
30s |
5m |
GOMDDOC_SERVER_HTTP_IDLE_TIMEOUT |
2m |
10m |
GOMDDOC_SERVER_HTTP_MAX_HEADER_MB |
1 |
10 |
GOMDDOC_SERVER_HTTP_SHUTDOWN_TIMEOUT |
1s |
none |
Durations use Go syntax (500ms, 45s, 2m). A zero, negative or out-of-range value in the first four falls back
to the default with a warning. The shutdown timeout is the grace period for in-flight requests on SIGINT and
SIGTERM; a negative value is an error and anything above 60s logs a warning.
Other environment variables
| Variable | Belongs to |
|---|---|
GOMDDOC_DOMAIN |
--domain on serve, preview and build; overrides meta.domain |
GOMDDOC_BUILD_OUTPUT |
build --output |
GOMDDOC_PREVIEW_OPEN |
preview --open |
GOMDDOC_DIR_INDEX |
preview --dir-index |
SSH_KNOWN_HOSTS |
Known-hosts file for SSH Git sources; defaults to ~/.ssh/known_hosts |
An empty variable counts as unset. A value that doesn't parse is ignored with a warning, and gomddoc doctor reports
it along with any GOMDDOC_* variable gomddoc doesn't recognise.
Command flags
Every command takes -h / --help and --version. The directory argument defaults to . and accepts a Git URL
everywhere except init, info and schema.
| Command | Flags |
|---|---|
serve |
-p/--port (:8080), --admin-port, -d/--domain, --git-key-file, --git-storage-dir, --pprof, --basic-auth-file |
preview |
-p/--port (:auto), -d/--domain, --open, --dir-index, --git-key-file, --git-storage-dir |
build |
-o/--output (build/site), -d/--domain, --git-key-file, --git-storage-dir |
mcp |
--git-key-file, --git-storage-dir |
doctor |
--json, --strict, -v/--verbose, --git-key-file, --git-storage-dir |
init |
-t/--theme (default) |
info |
--json |
schema |
none |
help |
[topic] [section], -s/--search |
preview always runs in dev mode and has no --admin-port, --pprof or --basic-auth-file. build refuses to
write into a non-empty directory it didn't create, and replaces its own previous output. init fails if
.gomddoc/config.yml already exists, and writes the theme name without installing the theme.
Frontmatter
Any page can start with a YAML block. These fields have built-in meaning:
---
title: Deployment Guide
description: Deploy the app with Docker and Kubernetes.
tags: [devops, docker]
date: 2026-01-15
author: Jane Doe
redirect_from:
- /old/deploy
features:
see_also: false
---
| Field | Effect |
|---|---|
title |
Page title, navigation label, search results, Open Graph. Falls back to the first # heading, then the file name |
description |
Meta description, Open Graph, Twitter card and search. Falls back to meta.description |
tags |
Tag chips, /tags/ pages, "See also", tag: search, /api/tags and MCP find_related. Lowercased and trimmed |
date |
YYYY-MM-DD or RFC 3339. JSON-LD datePublished, and the fallback date for the sitemap and feed |
author |
Author in the page's JSON-LD |
robots |
Per-page <meta name="robots">. noindex also drops the page from the sitemap and feed |
og_type |
Open Graph type; defaults to article |
lang |
The page's <html lang>, overriding language |
layout |
Render with layouts/<name>.html.tmpl, falling back to default |
redirect_from |
Old paths that 301 to this page in serve, and redirect pages in build |
features |
Per-page feature toggles, merged over theme.features |
Any other field is kept and available to templates as {{ index .Page.Meta "field" }}. gomddoc doctor reports
special fields with the wrong type, such as tags: foo instead of a list, and a tag containing / or \ is dropped.
Checking your configuration
Four commands read the configuration from the binary itself, so they can't drift from the version you run:
gomddoc info ./docs # every setting, and whether config.yml loads
gomddoc doctor ./docs --strict # every problem with a fix; non-zero exit for CI
gomddoc schema > .gomddoc/config.schema.json # JSON Schema for editor validation
gomddoc help configuration # the full configuration guide
info never fails on a broken config: it reports why the file didn't load and exits 0, which makes it the first thing
to run after an edit. doctor exits 1 on any error, or on any warning with --strict. --json on either command
prints the same document the MCP tools gomddoc_capabilities and gomddoc_doctor return.