Antora Project Structure Guide
Reference guide for setting up and organizing an Antora documentation project. Based on the domus-* ecosystem (15 spoke repos + 1 hub).
Standard Module Structure
Every Antora component follows this directory layout:
docs/ (1)
├── antora.yml (2)
└── modules/
└── ROOT/ (3)
├── nav.adoc (4)
├── pages/ (5)
│ ├── index.adoc
│ └── topic/
│ └── page.adoc
├── partials/ (6)
│ └── reusable-table.adoc
├── examples/ (7)
│ └── scripts/
│ └── example.sh
└── images/ (8)
└── diagrams/
└── architecture.svg
| 1 | docs/ — Antora start path (configured in antora.yml or playbook) |
| 2 | antora.yml — component descriptor (name, title, version, attributes) |
| 3 | ROOT — default module name. Most projects only need ROOT. |
| 4 | nav.adoc — sidebar navigation tree |
| 5 | pages/ — publishable content. Every .adoc here becomes a URL. |
| 6 | partials/ — reusable content fragments. Included via include::partial$file.adoc[] |
| 7 | examples/ — code snippets. Included via include::example$file.sh[] |
| 8 | images/ — images and diagrams. Referenced via image::file.svg[] |
antora.yml (Component Descriptor)
name: my-component # Used in cross-references: xref:my-component::page.adoc[]
title: My Documentation # Display name in UI
version: ~ # ~ = versionless (recommended for docs-as-code)
start_page: ROOT:index.adoc
nav:
- modules/ROOT/nav.adoc
asciidoc:
attributes:
# Global attributes available to ALL pages in this component
icons: font
source-highlighter: highlight.js
attribute-missing: warn # Catch typos in attribute references
# Custom attributes (use instead of hardcoding values)
domain: example.com
server-ip: '10.0.0.1'
author: Your Name
Images and Diagrams
Images go in modules/ROOT/images/. Organize with subdirectories:
images/
├── diagrams/ # D2, Mermaid, PlantUML rendered SVGs
│ ├── architecture/
│ ├── network/
│ └── security/
├── screenshots/ # UI screenshots
└── logos/ # Branding
Reference in pages:
// Simple image
image::diagrams/architecture/my-diagram.svg[Alt text,800]
// Image in collapsible block
[%collapsible]
====
image::screenshots/ise-dashboard.png[ISE Dashboard]
====
You do NOT need a separate images/ directory per module. One images/ directory at the module level is standard. Organize with subdirectories inside it.
|
Partials (Reusable Content)
Partials are content fragments included in multiple pages. Single source of truth.
partials/
├── tables/ # Reusable tables (project status, inventory)
├── trackers/ # Status trackers (priorities, blockers)
├── projects/ # Project documentation (modular lego pieces)
│ └── my-project/
│ ├── summary.adoc
│ ├── architecture.adoc
│ └── roadmap.adoc
└── nav/ # Navigation partials (modular sidebar)
Include syntax:
// Include a partial
include::partial$tables/project-status.adoc[]
// Include with tag filtering (selective sections)
include::partial$trackers/priorities.adoc[tag=p0]
// Cross-component partial (from another repo)
include::infra-ops::partial$vlan-table.adoc[]
Examples (Code Snippets)
Examples are reusable code blocks included in pages.
examples/
├── scripts/
│ └── backup.sh
├── configs/
│ └── nginx.conf
└── api/
└── curl-examples.sh
Include syntax:
// Include entire file
[source,bash]
include::example$scripts/backup.sh[]
// Include tagged region [source,bash]
include::example$api/curl-examples.sh[tag=auth]
Cross-References
// Same component (single colon or none)
xref:topic/page.adoc[Link Text]
// Different component (DOUBLE colon required)
xref:infra-ops::runbooks/deployment.adoc[Deployment Guide]
xref:ise-linux::02-pki/certificate-enrollment.adoc[Cert Enrollment]
// CRITICAL: Use full paths from pages/ root in nested directories
// WRONG: xref:sibling-page.adoc[Link] (relative — breaks in subdirs)
// RIGHT: xref:topic/subtopic/sibling-page.adoc[Link]
Navigation (nav.adoc)
// Inline navigation
* Section Title
** xref:pages/topic.adoc[Page Title]
*** xref:pages/topic/subtopic.adoc[Subtopic]
// Modular navigation (include partials for large navs)
* Projects
** xref:projects/index.adoc[Overview]
include::partial$nav/projects-chla.adoc[]
include::partial$nav/projects-personal.adoc[]
Hub-Spoke Architecture
For multi-repo documentation (like domus-*):
Hub: domus-docs (aggregator)
├── antora-playbook.yml # Lists ALL spoke repos
├── ui-bundle.zip # Custom UI theme
└── Deploys to: docs.example.com
Spoke: domus-captures # Component: captures
Spoke: domus-infra-ops # Component: infra-ops
Spoke: domus-ise-linux # Component: ise-linux
Spoke: domus-netapi-docs # Component: netapi
...
Playbook (hub):
content:
sources:
- url: https://github.com/user/domus-captures.git
branches: main
start_path: docs
- url: https://github.com/user/domus-infra-ops.git
branches: main
start_path: docs
Local build (single spoke):
content:
sources:
- url: .
branches: HEAD
worktrees: true
start_path: docs
Attributes — Never Hardcode
// WRONG — hardcoded values
Connect to `10.50.1.20` on port `9060`.
// RIGHT — use attributes from antora.yml
Connect to \{ise-ip} on port \{ers-port}.
// In code blocks, add subs=attributes+ to resolve attributes
[source,bash,subs=attributes+]
Quick Start: New Spoke Repo
mkdir -p docs/modules/ROOT/{pages,partials,examples,images}
Create docs/antora.yml:
name: my-component
title: My Documentation
version: ~
start_page: ROOT:index.adoc
nav:
- modules/ROOT/nav.adoc
asciidoc:
attributes:
icons: font
source-highlighter: highlight.js
attribute-missing: warn
Create docs/modules/ROOT/nav.adoc:
* xref:index.adoc[Home]
Create docs/modules/ROOT/pages/index.adoc:
= My Documentation
:description: What this component covers
:navtitle: Home
Welcome to the documentation.
Add to hub playbook, push, build.