Configuration Schema
site.yaml
# REQUIRED
title: string # Site title
# OPTIONAL (with defaults)
description: "" # string — Site description
url: "http://localhost:8000" # string — Canonical base URL
author:
name: "" # string — Author name
email: "" # string — Author email (optional)
metadata: {} # Record<string, string> — HTML meta tags
taxonomies: # string[] — Enabled taxonomy types
- "category"
- "tag"
routes: {} # Record<string, string> — Route aliases
redirects: {} # Record<string, string> — 301 redirects
home: null # string | null — Home page slug. Auto-detected if null.
cors_origins: [] # string[] — Extra origins allowed for cross-origin API requests
feed:
enabled: true # boolean — Generate /feed.xml and /atom.xml (default: true)
items: 20 # number — Most-recent dated pages to include (default: 20)
content: "summary" # "summary" | "full" — Feed item body (default: "summary")
sitemap:
exclude: [] # string[] — Route prefixes to omit from /sitemap.xml
changefreq: {} # Record<string, changefreq> — Per-route overrides (longest prefix wins)
# Valid changefreq values: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"
http_cache:
default_max_age: 0 # number — Browser max-age in seconds (0 = revalidate every time)
default_swr: 60 # number — Default stale-while-revalidate in seconds
rules: # Per-route overrides — longest matching prefix wins
- pattern: "/blog" # string — URL prefix or exact path
max_age: 3600 # number — Browser max-age override
stale_while_revalidate: 86400 # number — SWR override
- pattern: "/search"
no_store: true # boolean — Emit Cache-Control: no-store (disables all caching)
system.yaml
content:
dir: "content" # string — Content directory path
markdown:
extra: true # boolean — Extended markdown features
auto_links: true # boolean — Auto-link URLs
auto_url_links: true # boolean — Auto-link bare URLs
cache:
enabled: true # boolean
driver: "filesystem" # "memory" | "filesystem" | "kv"
lifetime: 3600 # number — Seconds
check: "file" # "file" | "hash" | "none"
images:
default_quality: 80 # number — 1-100
cache_dir: ".dune/cache/images" # string
allowed_sizes: # number[] — widths/heights allowed for on-the-fly processing
- 320
- 640
- 768
- 1024
- 1280
- 1536
- 1920
languages:
supported: ["en"] # string[] — Language codes
default: "en" # string — Must be in supported list
include_default_in_url: false # boolean — /en/page vs /page
typography:
orphan_protection: true # boolean — Insert before last word in paragraphs
search:
customFields: [] # string[] — Extra frontmatter field names to include in search index
# e.g. ["summary", "author", "series"]
page_cache:
enabled: false # boolean — Enable in-process rendered HTML cache (default: false)
max_entries: 500 # number — Max pages held in memory; oldest evicted when full (default: 500)
ttl: 30 # number — Seconds before an entry is re-rendered (default: 30)
warm: false # boolean — Pre-resolve all pages at startup (default: false)
debug: false # boolean
timezone: "UTC" # string — IANA timezone
admin.yaml (or admin: block in dune.config.ts)
admin:
enabled: true # boolean — Enable admin panel (default: true)
path: "/admin" # string — URL prefix for the admin panel
sessionLifetime: 86400 # number — Session lifetime in seconds (default: 86400 = 24 h)
dataDir: "data" # string — Persistent data directory (users, submissions, comments). Git-tracked.
runtimeDir: ".dune/admin" # string — Runtime data directory (sessions, revisions, staging, analytics). Not git-tracked.
maxRevisions: 50 # number — Maximum revisions retained per page (default: 50)
git_commit: false # boolean — Auto-commit to git after every page save/publish (default: false)
# Outbound webhooks — fired on content mutation events
webhooks:
- url: "https://hooks.example.com/content"
secret: "$WEBHOOK_SECRET" # string — HMAC-SHA256 signing secret ("$ENV_VAR" expansion supported)
label: "My integration" # string — Human-readable label for delivery logs (optional)
enabled: true # boolean — Disable without removing (default: true)
events: # WebhookContentEvent[] — which events trigger this endpoint
- onPageCreate
- onPageUpdate
- onPageDelete
- onWorkflowChange
# Incoming webhooks — let external systems trigger actions via POST /api/webhook/incoming
incoming_webhooks:
- token: "$DEPLOY_WEBHOOK_TOKEN" # string — pre-shared token ("$ENV_VAR" expansion supported)
actions: [rebuild] # Array<"rebuild" | "purge-cache">
- token: "$CACHE_WEBHOOK_TOKEN"
actions: [purge-cache]
Valid events values: onPageCreate, onPageUpdate, onPageDelete, onWorkflowChange. See Webhooks for full documentation.
Valid incoming webhook actions: rebuild (re-indexes content), purge-cache (clears the processed image cache).
dataDir contains user accounts, form submissions, and editorial comments — should be committed to version control. runtimeDir contains ephemeral session, revision, staging, and analytics data — should be in .gitignore.
theme config
Set in dune.config.ts or via config:
theme:
name: "default" # string — Active theme name
parent: null # string | null — Parent theme for inheritance
custom: {} # Record<string, unknown> — Theme-specific settings
plugins config
plugins:
plugin-name: # Plugin-specific config (arbitrary keys)
key: value
Validation
Run dune config:validate to check your config files. The validator produces actionable error messages:
✗ Config error in config/site.yaml:
→ site.taxonomies must be an array of strings
→ Got: "category, tag" (string)
→ Did you mean: ["category", "tag"]?