antora.yml and playbook keys, annotated

This reference documents the configuration surface you'll touch in a working docs-as-code setup: the component descriptor (antora.yml), the site playbook (antora-playbook.yml), and the resource IDs that connect content together. It covers the keys teams actually use; the upstream reference remains the exhaustive list.

Everything below applies to Antora 3.x.

The component descriptor: antora.yml

Lives at the root of the documentation component in each content repository (conventionally docs/antora.yml). Its contents are read per branch — that's how one repository publishes several versions.

Key Required What it controls
name yes The component's identity in URLs (/widget/…) and resource IDs (widget::page.adoc). Lowercase, stable, never renamed casually: renaming changes every URL and cross-component xref.
title yes The display name in the UI (navigation, component selector, page breadcrumbs). Safe to change anytime.
version yes The documented version, as a quoted string ('2.0', not 2.0; YAML reads the latter as a number). Use ~ for a component that shouldn't version at all (release notes, a glossary).
display_version no What the version selector shows, when it should differ from version, e.g. version: '2.0', display_version: '2.0 LTS'. URLs still use version.
prerelease no true keeps this version out of the "latest" slot: it builds and is selectable, but latest-version links keep resolving to the newest stable version. Typical on the development branch.
start_page no The page that answers for the component's root URL. Defaults to index.adoc in the ROOT module; set it only if you deliberately have no index.
nav no Ordered list of navigation files (e.g. modules/ROOT/nav.adoc). A module without an entry here still publishes pages; they're just unreachable from the sidebar.
asciidoc.attributes no AsciiDoc attributes scoped to this component and version, such as product names and version numbers for substitution in prose. Branch-varying values belong here, not in the playbook.

The standard directory layout

The descriptor sits on top of a fixed structure. The directory names are load-bearing — they're how Antora classifies files into families:

docs/
├── antora.yml
└── modules/
├── ROOT/ <- pages at the component root URL
│ ├── nav.adoc
│ ├── pages/ <- *.adoc -> one page each
│ ├── partials/ <- includable snippets (partial$)
│ ├── examples/ <- includable code samples (example$)
│ ├── images/ <- figures (image$)
│ └── attachments/ <- downloadable files (attachment$)
└── admin/ <- extra module -> /widget/2.0/admin/…
└── pages/

Only pages/ is special-cased into output; the other families publish nothing on their own and exist to be referenced.

The playbook: antora-playbook.yml

Lives in the playbook repository and defines one site build. One playbook, many components.

site

Key What it controls
site.title The site-wide title shown in the UI chrome.
site.url The site's public base URL. Required for a correct sitemap, canonical URLs, and (on subpath hosting like GitHub Pages project sites) correctly resolved assets.
site.start_page Which page answers at /, named by a resource ID like widget::index.adoc. Without it, the site root serves a generated 404.

content

Key What it controls
content.sources[].url A git URL (https://…) or local path (../widget; development only, since CI can't see it).
content.sources[].branches Exact names and globs selecting the branches to read: [main, v*]. Negations exclude: "!v1.0". HEAD means the current branch of a local repository, the right value while authoring.
content.sources[].tags Same selection language for tags, for teams that freeze docs at tags instead of branches.
content.sources[].start_path Where the component lives inside the repository (docs). Plural start_paths accepts a list or glob for monorepos holding several components.

ui

Key What it controls
ui.bundle.url The UI bundle zip: layout, styles, header/footer. The default-UI artifact URL serves a moving snapshot, hence the next key.
ui.bundle.snapshot true tells Antora to re-download the bundle when fetching instead of treating the cached copy as immutable. Set it whenever the URL's content can change in place.

runtime and output

Key What it controls
runtime.fetch true refreshes the git cache on every build (the --fetch CLI flag does the same per run). Without it, Antora trusts clones it has already made.
runtime.log.failure_level The build's strictness: error (or warn) makes Antora exit non-zero when anything at that level is logged, such as broken xrefs and missing includes. The key that makes CI trustworthy.
asciidoc.attributes Site-wide AsciiDoc attributes. Component-and-version-specific values belong in antora.yml instead.
asciidoc.extensions / antora.extensions Registered Asciidoctor extensions (e.g. @asciidoctor/tabs) and Antora extensions (e.g. the Lunr search extension), installed via npm in the playbook repository.
output.dir Where the site is written. Default: build/site.

Resource IDs

Every cross-reference and include names its target by resource ID, not file path. The full form:

version@component:module:family$relative/path.adoc

Every segment except the file is optional, and omitted segments default to "same as the current page", which is why the common cases stay short:

You write It means
xref:install.adoc[] Page in the same component, version, and module.
xref:admin:backup.adoc[] Page in the admin module, same component and version.
xref:widget::index.adoc[] Start page of the widget component, latest version (note the double colon: empty module = ROOT).
xref:1.0@widget::install.adoc[] Same, pinned to version 1.0.
include::partial$prereqs.adoc[] Snippet from this module's partials/.
include::example$config.yml[] File from this module's examples/.
image::diagram.png[Topology] Figure from this module's images/.

Two rules keep teams out of trouble:

  • Never link pages with relative file paths. Path links bypass validation and break when content moves between modules or versions. If it's a page, it's an xref:.
  • Omit the version unless you mean it. An unversioned xref follows the reader's current version (and the latest when crossing components), which is almost always the intent. A pinned 1.0@ reference is a deliberate, greppable exception.