How to Set Up MkDocs for GitHub Pages

June 2026 · Published by Amar Kumar

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

OptionProsCons
MkDocs + MaterialFast builds, great search, minimal JSPython toolchain required
DocusaurusReact ecosystem, versioningHeavier stack, more config
Hugo / JekyllVery fast, no PythonLess doc-specific defaults
Notion exportEasy for writersWeak 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 &copy; 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
FeatureEffect
navigation.tabsTop-level sections as horizontal tabs
navigation.sectionsSidebar groups with section headers
content.code.copyCopy button on code blocks
content.action.editEdit-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.

Custom branding via extra_css:

extra_css:
  - stylesheets/extra.css
/* docs/stylesheets/extra.css */
:root {
  --md-primary-fg-color: #2563eb;
}

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:

![Dashboard overview](assets/screenshots/dashboard.png)

<!-- from docs/guides/api-authentication.md -->
![Auth flow](../assets/diagrams/oauth-flow.png)

Do not use absolute /assets/... paths unless you understand MkDocs URL rewriting with use_directory_urls.

Local development workflow

CommandPurpose
mkdocs serveDev server with live reload
mkdocs serve --dirtyFaster reload — changed pages only
mkdocs build --strictProduction 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.

MethodBest forTrade-off
GitHub ActionsTeams, CI, strict buildsInitial workflow setup
mkdocs gh-deploySolo, quick startOpaque force-push
Manual site/Legacy reposBloated git history

Custom domain and HTTPS

  1. Create docs/CNAME containing docs.example.com — MkDocs copies it to the site root on build.
  2. DNS: CNAME record docsyourorg.github.io.
  3. GitHub repo Settings → Pages → Custom domain → enter the domain.
  4. Enable Enforce HTTPS once the certificate provisions.
  5. Update site_url to https://docs.example.com/.

Useful plugins

PluginPurpose
searchFull-text lunr index (client-side, no server)
minifySmaller HTML/CSS/JS
redirectsMeta redirects for renamed pages
git-revision-date-localized"Last updated" from Git history
mkdocstringsAuto 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

Common mistakes

MistakeFix
Wrong site_urlSet HTTPS production URL with trailing slash
Images 404 on GitHub PagesRelative paths from docs/
Nav points to missing fileRun mkdocs build --strict locally
Pages source mismatchAlign Settings → Pages with deploy method
Material not installedpip install mkdocs-material
Broken search on project sitessite_url must include repo path

Glossary

TermMeaning
MkDocsStatic site generator for Markdown documentation
MaterialPopular MkDocs theme with search, tabs, admonitions
mkdocs.ymlSite configuration file at repo root
site/Build output directory (static HTML)
lunrClient-side search index used by Material
strict modeFail 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