Check Runbook Links in CI
Run lychee against runbook Markdown on a schedule so dead dashboards, wiki pages, and pager links are fixed before an incident.
Task
Keep operational documentation links working so responders do not hit 404s in the middle of an outage.
When to use this
A runbook points to a dashboard that was renamed and a wiki page that was deleted. Nobody clicks those links during normal work, and the failure is discovered at 2 a.m. when the on-call engineer needs the dashboard most.
Prerequisites
- lychee 0.15 or newer installed locally or in CI
- Runbooks stored as Markdown in a Git repository
- CI with outbound network access
Steps
1. Add a lychee configuration that accepts redirects and temporary auth responses without hiding broken links.
max_concurrency = 8
timeout = 20
max_retries = 2
retry_wait_time = 2
accept = [200, 204, 301, 302, 403]
Expected output: The config is committed as .lychee.toml and applies to every local and CI run.
2. Run the checker against the runbook directory and review every reported failure.
lychee --config .lychee.toml 'docs/runbooks/**/*.md'Expected output: A report listing each broken or unreachable link with its source file and line number.
3. Run the check weekly and on every pull request that changes documentation.
- uses: lycheeverse/lychee-action@v2
with:
args: --config .lychee.toml docs/runbooks
fail: true
Expected output: Documentation pull requests with a dead link fail CI, and the weekly scheduled run catches link rot in unchanged files.
4. Fix or explicitly exclude links that require SSO, and track the exclusion with an owner.
printf '%s
' 'https://internal.acme.com/legacy-dashboard' >> .lycheeignore && lychee --config .lychee.toml 'docs/runbooks/**/*.md'Expected output: The SSO-only URL is excluded by policy, the run passes, and the exclusion is documented in the operations backlog.
Troubleshooting
Problem
Slow internal dashboards cause timeouts
Solution
Raise timeout and retries, or add the host to .lycheeignore with a documented reason; do not lower the whole check to a warning.
Problem
External sites return 403 to automated requests
Solution
Accept 403 in the config only if the page is known to be reachable in a browser; otherwise replace the link with a stable canonical source.
Limitations
- Links behind SSO cannot be validated by a CI runner and need manual review.
- A reachable page may still be outdated; the check validates availability, not content accuracy.
Link rot is an incident risk
Runbooks are used under pressure, when there is no time to search for the new dashboard URL. A dead link turns a documented recovery into improvisation. Link checking is cheap, deterministic, and catches the most common documentation failure: a resource moved or deleted without updating the references.
Configure for signal
The defaults are reasonable; the tuning that matters is timeout and accept. Internal dashboards can be slow, so allow a longer timeout and a couple of retries. Accept 403 only when you have confirmed the page works in a browser with a session; otherwise you are hiding a real failure. Keep fail: true in CI so the check blocks a merge rather than producing a warning nobody reads.
max_concurrency = 8
timeout = 20
max_retries = 2
accept = [200, 204, 301, 302, 403]
Run on two cadences
Pull request checks catch links broken by the change under review. A weekly scheduled run catches external link rot in files nobody edited. Run both against the runbook directory, not the whole repository, to keep runtime short and failures owned by the operations team.
Handle the exceptions deliberately
SSO-protected links cannot be checked by a runner. Add them to .lycheeignore with a comment and an owner, and review the list quarterly. Suppressing a link is a decision, not a default. When a dashboard moves, update the runbook and remove the exclusion in the same change.