How to Set Up MkDocs for GitHub Pages
MkDocs turns a folder of Markdown files into a fast, searchable documentation site. This guide walks through installation, configuration, the Material theme, navigation, plugins, local preview, and production deployment to GitHub Pages.
You have Markdown in a repo (or synced from cloud storage). You want a polished docs site with search, mobile layout, and a URL like docs.yourproduct.com — without maintaining a custom frontend. MkDocs is the standard answer: Python-based, config-driven, and designed exactly for technical documentation.
This article is a step-by-step setup guide, not a feature overview. By the end you will have a working site locally and a repeatable path to GitHub Pages.
Related pipeline guide: Publish a Knowledge Base to GitHub Pages.
Who is this for? Developers and technical writers who want a maintainable docs site from Markdown — especially teams already using GitHub for source control.
Typical setup time to first deployed page (hours, small team)
Why MkDocs
| Option | Pros | Cons |
|---|---|---|
| MkDocs + Material | Fast builds, great search, minimal JS | Python toolchain required |
| Docusaurus | React ecosystem, versioning | Heavier stack, more config |
| Hugo / Jekyll | Very fast, no Python | Less doc-specific defaults |
| Notion export | Easy for writers | Weak SEO, poor version control |
MkDocs wins when your source of truth is Markdown in Git and you want minutes-to-live deployment. The Material theme adds professional navigation, dark mode, and built-in search without a Node build step.
Prerequisites and installation
You need Python 3.9+ and pip. Use a virtual environment so MkDocs does not pollute system Python.
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install --upgrade pip
pip install mkdocs mkdocs-material
Verify with mkdocs --version. Pin versions in production:
# requirements-docs.txt
mkdocs>=1.6,<2
mkdocs-material>=9.5,<10
mkdocs-minify-plugin>=0.8,<1
mkdocs-redirects>=1.2,<2
Project structure
MkDocs expects a config file at the repo root. Markdown lives under docs/ by default.
my-docs/
├── docs/ ← all published .md pages
│ ├── index.md ← site homepage (/)
│ ├── getting-started/
│ │ ├── index.md
│ │ └── installation.md
│ ├── guides/
│ │ └── api-authentication.md
│ └── assets/
│ └── screenshots/
├── mkdocs.yml ← site configuration
├── requirements-docs.txt
├── .github/workflows/docs.yml
└── site/ ← build output (gitignored)
Rules: one .md file = one page; keep images under docs/assets/; never commit site/ unless you deploy by pushing build output to gh-pages.
Create your first site
mkdocs new my-docs
cd my-docs
mkdocs serve
Open http://127.0.0.1:8000. Edit docs/index.md — the browser auto-reloads.
# mkdocs.yml — minimal starting config
site_name: My Product Docs
site_url: https://yourorg.github.io/my-docs/
repo_url: https://github.com/yourorg/my-docs
edit_uri: edit/main/docs/
theme:
name: material
nav:
- Home: index.md
- Getting started:
- Overview: getting-started/index.md
- Installation: getting-started/installation.md
mkdocs.yml in detail
The config file controls theme, nav, plugins, extensions, metadata, and build behavior.
Site metadata
site_name: My Product Docs
site_url: https://docs.example.com/ # trailing slash matters
site_description: Official documentation for My Product API and SDK.
copyright: Copyright © 2026 Example Inc.
repo_url: https://github.com/yourorg/my-docs
edit_uri: edit/main/docs/
site_url drives sitemap generation, Open Graph tags, and absolute links. Set it to your final public URL, not localhost.
Theme block
theme:
name: material
logo: assets/logo.svg
palette:
- scheme: default
primary: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: indigo
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- navigation.tabs
- navigation.sections
- search.suggest
- content.code.copy
- content.action.edit
| Feature | Effect |
|---|---|
navigation.tabs | Top-level sections as horizontal tabs |
navigation.sections | Sidebar groups with section headers |
content.code.copy | Copy button on code blocks |
content.action.edit | Edit-on-GitHub button per page |
Navigation
nav:
- Home: index.md
- Getting started:
- getting-started/index.md
- Install: getting-started/installation.md
- Guides:
- guides/api-authentication.md
- guides/webhooks.md
- Changelog: changelog.md
Omit nav and MkDocs auto-discovers files alphabetically — usually not what you want. External links work: API: https://api.example.com/docs.
Markdown extensions and plugins
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- toc:
permalink: true
plugins:
- search:
lang: en
- minify:
minify_html: true
- redirects:
redirect_maps:
old-page.md: new-page.md
strict: true # fail build on warnings — use in CI
Approximate build time vs page count (Material + minify, CI runner)
Material for MkDocs theme
Install separately with pip install mkdocs-material. It replaces the default theme entirely.
- Instant full-text search with highlighting
- Responsive layout and dark mode
- Admonitions, Mermaid diagrams, tabbed content
- Versioning support for multiple doc releases
Custom branding via extra_css:
extra_css:
- stylesheets/extra.css
/* docs/stylesheets/extra.css */
:root {
--md-primary-fg-color: #2563eb;
}
Navigation patterns
Small projects — flat nav with a handful of pages. Most products — folders with index.md per section; enable navigation.sections and navigation.indexes. Large repos (50+ pages) — generate nav in CI or use mkdocs-awesome-pages-plugin with per-folder .pages files.
Split guides (narrative) from reference (API specs). Users search differently; nav should match mental models.
Markdown extensions and admonitions
!!! note "Beta feature"
This endpoint is available on Pro plans only.
!!! warning
Rotating your API key invalidates active sessions immediately.
=== "Python"
```python
import requests
r = requests.get("https://api.example.com/v1/users")
```
=== "curl"
```bash
curl -H "Authorization: Bearer $TOKEN" \
https://api.example.com/v1/users
```
Mermaid diagrams: enable a custom fence in pymdownx.superfences, then write fenced mermaid blocks in Markdown.
Images, assets, and static files
Place files under docs/. Reference with relative paths from the current page:

<!-- from docs/guides/api-authentication.md -->

Do not use absolute /assets/... paths unless you understand MkDocs URL rewriting with use_directory_urls.
Local development workflow
| Command | Purpose |
|---|---|
mkdocs serve | Dev server with live reload |
mkdocs serve --dirty | Faster reload — changed pages only |
mkdocs build --strict | Production build; fail on warnings |
Add site/ and .venv/ to .gitignore.
Build and inspect output
mkdocs build --strict
# site/ now contains static HTML + search/search_index.json
Inspect site/sitemap.xml (requires site_url). Broken internal links surface as warnings — errors with --strict.
Deploy to GitHub Pages
Three common methods — pick one and stick with it.
Recommended adoption by team size (informal survey pattern)
Method A — GitHub Actions (recommended)
Build in CI; deploy the site/ artifact. Reproducible, reviewable, no force-push to gh-pages.
# .github/workflows/docs.yml
name: Deploy docs
on:
push:
branches: [main]
paths: ["docs/**", "mkdocs.yml", "requirements-docs.txt"]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- run: pip install -r requirements-docs.txt
- run: mkdocs build --strict
- uses: actions/upload-pages-artifact@v3
with:
path: site
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
steps:
- uses: actions/deploy-pages@v4
Repo Settings → Pages → Build and deployment → GitHub Actions.
Method B — mkdocs gh-deploy
mkdocs gh-deploy --force
One command pushes site/ to the gh-pages branch. Simple for solo devs; harder to review diffs.
Method C — Manual site/ commit
Commit build output to a branch. Works but couples source and artifacts — generally avoid.
| Method | Best for | Trade-off |
|---|---|---|
| GitHub Actions | Teams, CI, strict builds | Initial workflow setup |
mkdocs gh-deploy | Solo, quick start | Opaque force-push |
Manual site/ | Legacy repos | Bloated git history |
Custom domain and HTTPS
- Create
docs/CNAMEcontainingdocs.example.com— MkDocs copies it to the site root on build. - DNS:
CNAMErecorddocs→yourorg.github.io. - GitHub repo Settings → Pages → Custom domain → enter the domain.
- Enable Enforce HTTPS once the certificate provisions.
- Update
site_urltohttps://docs.example.com/.
Useful plugins
| Plugin | Purpose |
|---|---|
search | Full-text lunr index (client-side, no server) |
minify | Smaller HTML/CSS/JS |
redirects | Meta redirects for renamed pages |
git-revision-date-localized | "Last updated" from Git history |
mkdocstrings | Auto API docs from Python docstrings |
Docs + RAG: keeping HTML and vectors aligned
If the same Markdown powers a chatbot and a public site, treat one folder as the publish contract (docs/ or a master/ mirror). A sync job can chunk and embed changed pages for vector search, run mkdocs build on the same sources, and deploy HTML to GitHub Pages.
See Publish a Knowledge Base to GitHub Pages for the full dual-output pipeline.
One Markdown source → static site + vector index (typical sync job stages)
CI checklist
- Python version pinned in workflow (3.11 or 3.12)
requirements-docs.txtwith pinned MkDocs + Materialmkdocs build --strictin CIsite_urlmatches production URLdocs/CNAMEpresent if using custom domain- Path filters on workflow (only run when docs change)
.gitignoreincludessite/and.venv/
Common mistakes
| Mistake | Fix |
|---|---|
Wrong site_url | Set HTTPS production URL with trailing slash |
| Images 404 on GitHub Pages | Relative paths from docs/ |
| Nav points to missing file | Run mkdocs build --strict locally |
| Pages source mismatch | Align Settings → Pages with deploy method |
| Material not installed | pip install mkdocs-material |
| Broken search on project sites | site_url must include repo path |
Glossary
| Term | Meaning |
|---|---|
| MkDocs | Static site generator for Markdown documentation |
| Material | Popular MkDocs theme with search, tabs, admonitions |
| mkdocs.yml | Site configuration file at repo root |
| site/ | Build output directory (static HTML) |
| lunr | Client-side search index used by Material |
| strict mode | Fail build on warnings (broken links, etc.) |
Start with mkdocs new, swap in Material, define nav, enable strict, and deploy via GitHub Actions. You get a maintainable docs site that scales from ten pages to hundreds.
Related: Publish a Knowledge Base to GitHub Pages · How to Build a RAG Chatbot