Publish the site with GitHub Actions
This how-to guide wires your Antora playbook repository into GitHub Actions
so that every merge to main rebuilds the site and publishes it to GitHub
Pages — with broken cross-references failing the build instead of shipping.
"The published docs are stale" stops being a possible state of the world.
It assumes you have a working playbook repository (the
tutorial builds one) hosted on GitHub,
with content sources reachable over https://.
Before you start
- Your playbook's
content.sourcesentries must usehttps://git URLs, not local paths. CI has no../widgetto read. - In the repository's Settings → Pages, set the source to GitHub Actions.
1. Pin the Antora version
CI must not float to whatever Antora published last night. In the playbook
repository, make the dependencies explicit in package.json:
npm install --save-exact @antora/cli@3.1.14 @antora/site-generator@3.1.14
Commit both package.json and package-lock.json. The workflow will use
npm ci, which installs exactly the locked versions: same input, same
site, every run.
2. Make broken references fail the build
By default Antora logs unresolved xrefs and missing includes as errors but
still exits successfully. For CI you want the honest version. Add this to
antora-playbook.yml:
runtime:
log:
failure_level: error
With failure_level: error, any logged error (a typo'd xref, a missing
partial, a dangling nav entry) makes antora exit non-zero, which fails
the workflow, which blocks the merge. This single key is most of the quality
story of docs as code; set it before anything else depends on the pipeline.
Also set the site's public URL, which Antora needs to generate the sitemap and canonical links:
site:
url: https://your-org.github.io/docs-site
3. Add the workflow
Create .github/workflows/publish.yml in the playbook repository:
name: Publish docs
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install Antora
run: npm ci
- name: Build the site
run: npx antora --fetch antora-playbook.yml
- uses: actions/upload-pages-artifact@v3
with:
path: build/site
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
The pieces worth understanding rather than pasting:
--fetchtells Antora to refresh its git cache instead of trusting a previous clone. On a throwaway CI runner there usually is no cache, but the flag also protects you on self-hosted runners where there is.workflow_dispatchgives you a manual "rebuild now" button, useful when a content repository merged a docs change and you want the site to pick it up without waiting for a playbook commit (see the note below).- The permissions block is the Pages deployment contract:
pages: writeandid-token: writeare whatdeploy-pagesneeds.
4. Rebuild when content changes
The workflow above triggers on pushes to the playbook repository, but most docs changes land in content repositories. Close the loop by having each content repository poke the playbook repo when docs change. In the content repository:
name: Trigger docs publish
on:
push:
branches: [main]
paths: ["docs/**"]
jobs:
trigger:
runs-on: ubuntu-latest
steps:
- run: gh workflow run publish.yml --repo your-org/docs-site
env:
GH_TOKEN: ${{ secrets.DOCS_SITE_TRIGGER_TOKEN }}
DOCS_SITE_TRIGGER_TOKEN is a fine-grained personal access token (or a
GitHub App token) with permission to run workflows on the playbook
repository. The paths filter keeps code-only merges from rebuilding the
docs site for nothing.
5. Authenticate against private content repositories
If a content source is private, the CI build needs credentials to clone it.
Antora reads them from the GIT_CREDENTIALS environment variable. Add a
token with read access to the content repositories as a secret on the
playbook repo, then extend the build step:
- name: Build the site
run: npx antora --fetch antora-playbook.yml
env:
GIT_CREDENTIALS: https://token:${{ secrets.CONTENT_READ_TOKEN }}@github.com
Keep the playbook's content.sources URLs clean; credentials belong in the
environment, never committed to the playbook.
Troubleshooting
- The build fails fetching the UI bundle. The default-UI URL serves a
snapshot artifact, which is why the playbook marks it
snapshot: true. If your network policy blocks gitlab.com, download the bundle once, commit it to the playbook repo (or publish it as a release asset), and pointui.bundle.urlat that instead. - The site builds but styles are missing on Pages. Your
site.urlis wrong or absent. Project pages live under a subpath (/docs-site/), and the UI resolves assets relative to the site URL. - A content change merged but the site didn't update. The trigger
workflow in step 4 isn't installed in that content repository, or its
pathsfilter didn't match. Run the publish workflow manually (workflow_dispatch) while you fix the trigger. - The build fails on an xref you just removed the target of. That's the
pipeline working. Fix the reference — don't lower
failure_level.
Related pages
- Version your docs with branches: the branches this pipeline will happily publish side by side.
- antora.yml and playbook keys, annotated: every key used above, in context.