Source for https://openobserve.ai/docs, built with Fumadocs on Next.js and exported as static HTML.
Requires Node 22+ and pnpm.
pnpm install
pnpm devOpen http://localhost:3000/docs. The /docs prefix is part of the dev server
too, so local URLs match production exactly.
To produce the production build:
pnpm build # writes ./outAlternatively, open the repo in a VS Code dev container.
Copy .devcontainer/devcontainer.json.tpl to .devcontainer/devcontainer.json
and fix or remove the mounts section first, then run pnpm install && pnpm dev
inside the container and forward port 3000.
Pages live in docs/ as Markdown, and URLs mirror the file layout:
docs/ingestion/logs/vector.md is served at /docs/ingestion/logs/vector/.
A docs/<folder>/index.md becomes /docs/<folder>/.
---
title: Vector # short label: sidebar, breadcrumbs, search
metaTitle: Ship logs with Vector… # optional long <title>, for search engines
description: … # meta description
---title is what readers see in navigation, so keep it short. Put the
search-engine-facing headline in metaTitle; when it's omitted, title is used
for the <title> tag as well.
A page's own # H1 in the body is rendered as written. If a page has no # H1
at all, the frontmatter title is rendered as the heading instead, so a page
never ends up with none — matching what Material for MkDocs did.
Each folder's meta.json sets its title and the order of its pages:
{
"title": "Ingestion",
"pages": ["index", "logs", "metrics", "---Advanced---", "otlp"]
}Entries are file names without the extension, or folder names.
"---Label---" inserts a group heading and "[Text](/some/page/)" adds a
cross-link. A page missing from meta.json is still reachable by URL but is
hidden from the sidebar. A folder with no meta.json is sorted alphabetically.
These use remark directives:
:::note[Optional title]
An informational callout. Types: note, info, tip, warning, danger, success, question.
:::
:::accordion[Step 1: Install the CLI]
A collapsible section. Renders as a native `<details>`, so its content stays in
the HTML for search engines even while collapsed.
:::
::::tabs
:::tab[macOS]
```sh
brew install openobserve
```
:::
:::tab[Linux]
```sh
apt install openobserve
```
:::
::::Nesting requires the outer fence to have more colons than the inner one, as
in the ::::tabs / :::tab pair above.
- Links between pages are written as relative
.mdpaths ([Vector](../logs/vector.md)) so they stay clickable on GitHub. They're rewritten to real URLs at build time. - Images are referenced relative to the page (
). Everything non-Markdown underdocs/is mirrored intopublic/byscripts/copy-assets.mjson every build —public/is generated and gitignored, so always edit the original underdocs/. - Code fences support Shiki meta:
lineNumbers,lineNumbers=15,title="file.yaml", and{4,9,20-24}to highlight lines. An unrecognised language falls back to plain text rather than failing the build.
pnpm build writes a fully static site to out/, which is synced to S3 and
served through CloudFront:
| Branch | Target |
|---|---|
main |
production — https://openobserve.ai/docs |
dev |
staging |
See .github/workflows/. deploy-docs.sh and deploy-docs-staging.sh do the
same thing manually, against the same buckets and distributions, and are the
only supported way to deploy by hand. The one difference is that the workflows
pass --delete to aws s3 sync and the scripts do not, so a manual deploy will
not clear files that no longer exist in the build.
Every path sets an explicit content type on out/api/search: the search index
has no file extension, so without it S3 labels it binary/octet-stream and
CloudFront serves ~37 MB uncompressed instead of ~4 MB gzipped.
CI checks out with fetch-depth: 0 because each page's "last updated" date is
read from git history. The same history feeds <lastmod> in the sitemap and the
article:modified_time tag, so a shallow clone degrades those too.
scripts/post-export.mjs writes out/sitemap.xml (plus a gzipped copy) with one
entry per page. This is not optional: https://openobserve.ai/sitemap-index.xml
points at https://openobserve.ai/docs/sitemap.xml, and the deploy's
aws s3 sync --delete would otherwise remove the file that is already there.
Redirect stubs and the 404 are excluded from it, and both carry noindex.
Each page emits a canonical URL, a meta description, Open Graph and Twitter card
tags, article:modified_time, and JSON-LD (BreadcrumbList + TechArticle).
The breadcrumb data is built from the page tree, so it lists the same trail the
reader sees rather than a flat Docs > Page.
Images are handled in lib/remark/docs-images.ts: every one gets its intrinsic
width/height read from the file at build time so the layout doesn't shift as
it loads, and the first image on a page is fetched eagerly at high priority
because it is the likely largest-contentful-paint element.
Google Tag Manager, GA4, the Segment proxy and OpenObserve RUM load only in
production builds served from a non-localhost hostname, so local work never
reports into production analytics. Append ?analytics=1 to force them on for
verification — expect console errors from the third-party tags, which reject a
localhost origin.
| Path | What it is |
|---|---|
docs/ |
the content, plus its images and meta.json nav files |
app/, components/ |
the Next.js app and MDX components |
lib/remark/ |
build-time plugins (link/image rewriting, directives) |
scripts/ |
asset copying and the post-export pass (llms.txt, raw .md, redirects) |
scripts/migration/ |
one-time scripts that converted this site from Material for MkDocs; kept for reference, not part of the build |
source.config.ts |
Fumadocs collection and MDX pipeline config |