Configuration
Configuration lives in one config.kdl file at the project root, written in KDL. Every setting has a default, so you only write what you want to change.
Example Configuration
site "My Site"
url "https://example.com"
author "Ada"
lang "en"
paths {
content "content"
dist "public"
assets "assets"
static "static"
templates "templates"
}
collections {
posts sort="date" reverse=#true paginate=10 template="post.typ"
}
taxonomies {
tags index=#true
}
output {
urls "clean"
clean #true
html { pretty #true; meta #true; anchors #true }
images { lazy #true; extract #true; responsive { widths 480 960 1440 }; optimize { png; jpeg quality=82 } }
assets { minify #true; bundle #true; fingerprint #true }
search { formats "json"; fields "title" "body" "tags" }
feed { formats "rss" "atom"; limit 20 }
}
The html block controls the emitted markup (pretty formatting, embed to inline assets as data: URIs, meta for SEO and social tags, and anchors to give every heading a slug id for deep links); the images block handles lazy loading, externalizing embedded images, responsive srcset variants, and per-format optimization. See meta and images.
Every field has a sensible default, so a minimal config.kdl is just site "My Site". The rest overrides only what you name.
The blocks
paths- Where content, output, assets, templates, and the
staticpassthrough directory live. Files understatic/are copied verbatim to the output root (no processing, no fingerprint) for arobots.txtoverride,.well-known/, aCNAME, or aninstall.sh. client- Build-time constants exposed to client JavaScript through the
baudelaire:configvirtual module. collections- Per-group sorting, permalinks, pagination, and the default template. See pagination.
taxonomies- Group pages by a frontmatter list such as
tags. See taxonomies. output- Everything the build emits: the URL style (
urls "clean"for directory-per-page permalinks,urls "flat"for.htmlfiles), whether to sweep orphaned files from the output directory (clean, on by default), HTML options, the asset pipeline, search, feeds and sitemap,robots.txt, andllms.txt. hooks- Run external tools around the build. See build hooks.
NOTE
A misspelled key is a hard error with a “did you mean?” suggestion: the parser knows every valid key, so typos never silently do nothing.Subdirectory hosting
Give url a path and the whole site is served from it:
url "https://user.codeberg.page/project"
Every on-page URL, links, assets, redirects, the search client, then resolves under /project, and the absolute URLs in the sitemap, feeds, and canonical tags carry it too. Only the emitted URLs shift: the files on disk keep their plain layout (public/guide/index.html, never public/project/...), so the host maps the path back for you. baudelaire serve previews under the same path, and --base-url overrides it per build.
NOTE
A root domain (url "https://example.com") has no path, so nothing is prefixed. Data files a client reads directly, search.json and the baudelaire:* modules, keep canonical root-relative URLs; the bundled search client applies the base for you.
The client block, live
This site’s own client { } block, read straight from the baudelaire:config module and printed by a few lines of script, the same object your bundle would import:
// enable JavaScript to load baudelaire:config
Profiles
A profiles block holds named overlays applied with --profile:
profiles {
dev {
draft { build #true }
output { assets { minify #false; bundle #false; fingerprint #false } }
}
}
baudelaire build --profile dev then builds drafts and skips the asset pipeline for faster local iteration. Common flags (--drafts, --base-url, --out, --no-cache) can also be passed directly; see the CLI reference.
Environment variables
Any string value can pull from the environment with ${VAR}, with an optional ${VAR:-default} fallback. Handy for secrets and per-deploy settings you don’t want committed:
url "${SITE_URL:-http://localhost:1821}"
typst {
inputs {
environment "${DEPLOY_ENV:-development}"
}
}
An unset variable with no default expands to an empty string. Expansion happens as the config is parsed, so it works in every string: paths, hook commands, inputs, and more.
KDL niceties
The config is plain KDL 2.0, and a few of its features make editing pleasant:
- Optional quotes
- simple values need no quotes.
sort=date,future #true, andtemplate=post.typall work; reach for quotes only when a value has spaces or punctuation, like a URL. - Slashdash
- prefix any node with
/-to comment it out whole./-search { }turns search off without deleting your settings, which beats commenting every line. - Raw and multi-line strings
#".."#and"""let a hook command or a summary hold quotes and newlines with no escaping.
// disable search for now, but keep the settings
/-search {
formats "json"
}
// a raw string keeps the inner quotes intact
hooks {
before #"tailwindcss -i "assets/app.css" -o "assets/style.css" --minify"#
}
TIP
Slashdash and block-presence toggles compose nicely: arobots or llms block turns the feature on just by existing, and a /- in front turns it back off, no other edit required.