Output and deploy
What the build writes and how to serve it with nginx or Docker.
What the build writes
Let's say you have a folder of md files like this:
docs/
_public
index.md
sync.md
self-hosting.md
self-hosting/
https.md
bakemd.json
The build writes this:
dist/
index.html /
sync.html /sync
self-hosting.html /self-hosting
self-hosting/
https.html /self-hosting/https
assets/ hashed CSS, JS and fonts
sitemap.xml
llms.txt
robots.txt only with "robots": true
favicon.svg everything from _public/, as is
Pages are page.html, not page/index.html, so the server needs to map
/sync to sync.html. Most static hosts do this out of the box.
Example with GitHub Pages
Set site to the Pages URL, https://you.github.io/repo. The /repo part
becomes the prefix for every link and asset, so nothing else changes. In the
repo settings, set the Pages source to GitHub Actions and add a workflow that
runs the build and uploads the output folder:
- run: npx bakemd build docs --out dist
- uses: actions/upload-pages-artifact@v3
with:
path: dist
- uses: actions/deploy-pages@v4
Pages serves sync.html for /sync on its own.
Example with nginx
nginx config
Example nginx config:
location / {
try_files $uri.html $uri $uri/ =404;
add_header Cache-Control "no-cache";
}
Docker
The first stage builds. The second is nginx with the files.
FROM node:22-slim AS build
WORKDIR /app
COPY docs docs
RUN npx bakemd build docs --out dist
FROM nginx:1.27-alpine
COPY --from=build /app/node_modules/bakemd/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
Head tags and structured data
Every page gets a <title>, meta description, canonical link, Open Graph and
Twitter card tags, and a JSON-LD block.
The home page is a WebSite; every
other page is a TechArticle with a BreadcrumbList back to the root. All of
it is filled from bakemd.json and the page's frontmatter, so there is
nothing to configure.
llms.txt follows llmstxt.org: the site name and
description, then one line per page with its URL and description.
robots.txt is opt-in with "robots": true. It allows every crawler and
points at the sitemap. Crawlers only read it at the domain root, so skip it
when site has a path, as on GitHub Pages.