Share dbt docs with people who don't have dbt
dbt docs generate && dbt docs serve is one of the nicest things in the
analytics-engineering toolchain — a browsable catalog of every model, column,
test and lineage edge in your project, generated from code you already wrote.
And it runs on localhost:8080, on your laptop, for you.
The moment someone else needs it — an analyst asking what
fct_orders.net_revenue excludes, a PM checking which dashboards break if you
deprecate a source, an auditor asking what feeds a revenue number — you're
back to screenshots, or to standing up a hosting story for a static site.
The three ways teams solve it, and what each costs
dbt Cloud hosted docs. Works, always current, and requires a seat for every reader. Analysts might have one; the PM, the finance partner and the external auditor won't.
Self-hosted static site (S3, GitHub Pages, Netlify, Cloudflare Pages). Copy
target/ from CI to a bucket after every run. This is the most common answer
and it's a reasonable one — you get the full interactive lineage graph. Two
things to think about before you do it:
- Access control is coarse. A public bucket publishes your model names, column descriptions and warehouse structure to anyone who finds the URL. Your lineage graph is a map of your business. Pages on a private repo is public unless you're on GitHub Enterprise Cloud — more on that trade-off.
- There's still nowhere to respond. The question "what does
net_revenueexclude?" has to leave the docs site to get asked, and the answer never comes back to the docs.
Publish a catalog digest to a shareable document. A stable URL, real visibility levels, and a comment thread anchored to the model being questioned. That's the pattern below.
Publishing from your dbt CI job
manifest.json and catalog.json contain everything: every model, its
description, its columns and their descriptions, its tests, its freshness, and
the full dependency graph. A short script turns them into a plain HTML catalog
— the surface stakeholders actually read.
dbt build
dbt docs generate
python3 scripts/dbt_catalog_digest.py \
target/manifest.json target/catalog.json > catalog.html
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html catalog.html \
--arg title "Data catalog — $(date +%F)" '{title: $title, html: $html}')"
PATCH against one report id means one URL for the catalog, with a
revision per run. Attach manifest.json and catalog.json themselves as
assets if you want the raw artifacts to travel with the document.
Wiring for your provider: GitHub Actions, GitLab CI, Jenkins, CircleCI.
Two things this unlocks that hosting doesn't
Missing descriptions get filled in by the people who know. The fastest way
to find out that a model's description is wrong is to put it in front of the
person who uses that number and let them highlight it. An anchored thread on
fct_orders.net_revenue — "this excludes refunds, right? our board deck
assumes it doesn't" — is a documentation bug report attached to the exact line
it's about. Copy the resolved answer back into your .yml and the catalog
improves permanently. See commenting on HTML.
Diffs between deploys. Two revisions of the catalog can be compared directly, which turns "did anything change in the semantic layer this sprint?" into a diff rather than an archaeology project. Schema changes that would otherwise surface as a broken dashboard show up as a line that moved.
Keeping it current without thinking about it
If your catalog should refresh on a schedule rather than only on deploy, routines run it for you — a scheduled refresh that re-publishes the report and notifies subscribers when the content actually changed. See scheduled HTML reports.
The honest limits
- The interactive lineage graph is script-driven, and Comma strips
<script>on write. If people navigate the DAG visually every day, hosttarget/and use the digest for the stakeholder-facing catalog. Both can coexist; they serve different readers. - HTML body cap is 5 MB. A big project's full column catalog can approach it — filter to exposed or marted models, or split by domain, which is better reading anyway.
- Keep it team-visible. Model and column names describe your business.
Default to
teamor domain-gated, not public — see the sharing model.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.