# MkDocs: Project Documentation from Markdown

LLMS index: [llms.txt](/en/llms.txt)

---

Documentation in a repository ages faster than anyone reads it: README links lead nowhere, sections are scattered across `docs/`, `wiki/`, and Confluence, and site search doesn't work. MkDocs solves this predictably — it takes a folder of `.md` files and builds a static site. One config, one command for the deploy, familiar Markdown.

## What is MkDocs

MkDocs is a static site generator for documentation written in Python. Input: a directory of Markdown files and a YAML config. Output: a ready `site/` directory with HTML, served by any web server or hosted on GitHub Pages, GitLab Pages, S3. MkDocs core handles rendering; the theme defines look and features. The de-facto standard is [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/).

> [!NOTE]
> MkDocs does not use Jinja templates and does not require a database. It is static content that builds locally or in CI in a few seconds.

## Installation

The minimum requirement is Python 3.8+. Install into a virtual environment to avoid polluting the system pip.

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install mkdocs
```

Verification:

```bash
mkdocs --version
```

Typical output is `mkdocs, version 1.6.x`. The version matters because themes and plugins often require a specific range.

Useful packages installed alongside the core or separately:

| Package | Purpose |
| --- | --- |
| `mkdocs-material` | Material theme, navigation, search, tabs |
| `mkdocstrings` | Documentation generated from Python docstrings |
| `pymdown-extensions` | Additional Markdown extensions for Material |
| `mkdocs-minify-plugin` | HTML/CSS/JS minification in `site/` |

```bash
pip install mkdocs-material mkdocstrings[pymdownx]
```

> [!TIP]
> Pin theme and plugin versions in `requirements.txt`. Material breaks compatibility between minor releases, as does `mkdocstrings`.

## Creating a Project

`mkdocs new` scaffolds the project:

```bash
mkdocs new my-docs
cd my-docs
```

This creates a `docs/` directory with `index.md` and an empty `mkdocs.yml`. That is the working minimum — nothing else is mandatory.

```bash
tree my-docs
```

```
my-docs
├── docs
│   └── index.md
└── mkdocs.yml
```

## Directory Structure

`docs/` is the single source of Markdown. The directory hierarchy maps directly to URLs. The file `docs/guide/install.md` becomes `/guide/install/`. An `index.md` in the root of `docs/` is the landing page.

```
docs/
├── index.md
├── guide/
│   ├── install.md
│   └── config.md
├── reference/
│   └── cli.md
└── about.md
```

The site builds into the `site/` directory next to `mkdocs.yml`. This directory is a build artifact — it is committed only for manual deploys, and usually built by CI.

## Configuration mkdocs.yml

A minimal working config:

```yaml
site_name: My Project Docs
site_url: https://example.com/docs/
docs_dir: docs
site_dir: site

theme:
  name: material
```

The full set of keys actually used in production:

| Key | Purpose |
| --- | --- |
| `site_name` | Site title and default `<title>` |
| `site_url` | Canonical URL, required for `sitemap.xml` and `robots.txt` |
| `site_description` | Description, goes into meta tags |
| `docs_dir` | Directory with Markdown, defaults to `docs` |
| `site_dir` | Where HTML is built, defaults to `site` |
| `theme` | Theme and its parameters |
| `nav` | Explicit navigation, overrides auto-discovery |
| `plugins` | Plugins in load order |
| `markdown_extensions` | Enabled Markdown extensions |
| `extra` | Arbitrary variables read by the theme |

Example with navigation, extensions, and plugins:

```yaml
site_name: Service Docs
site_url: https://docs.example.com/
repo_url: https://github.com/example/service

theme:
  name: material
  features:
    - navigation.tabs
    - navigation.sections
    - search.highlight
    - content.code.copy
  palette:
    - scheme: default
      toggle:
        icon: material/brightness-7
        name: Dark theme
    - scheme: slate
      toggle:
        icon: material/brightness-4
        name: Light theme

nav:
  - Home: index.md
  - Guide:
      - Installation: guide/install.md
      - Configuration: guide/config.md
  - Reference:
      - CLI: reference/cli.md

markdown_extensions:
  - admonition
  - tables
  - toc:
      permalink: true
  - pymdownx.highlight:
      anchor_linenums: true
  - pymdownx.superfences
  - pymdownx.tabbed:
      alternate_style: true

plugins:
  - search
```

> [!WARNING]
> Enable `search` explicitly when using the `plugins` list. In newer Material versions it is no longer pulled in automatically from the theme.

## Content

Markdown files are standard CommonMark with extensions. Useful constructs that work out of the box with the extensions enabled in the example above.

Admonitions:

```markdown
> [!NOTE]
> A brief note for the reader.

> [!WARNING]
> This action may cause data loss.
```

Tabs with `pymdownx.tabbed`:

````markdown
=== "Linux"

    ```bash
    sudo apt install foo
    ```

=== "macOS"

    ```bash
    brew install foo
    ```
````

Code highlighting with language specified:

````markdown
```python
from mkdocs import config
print(config.DEFAULT_SCHEMA.keys())
```
````

> [!TIP]
> Use heading anchors to link between pages. Material renders a `#` icon next to the heading when `toc.permalink: true` is set.

Internal links are relative paths from the current file:

```markdown
See the [configuration section](config.md).
```

External links open in the same tab by default. To open in a new tab:

```markdown
[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/){target=_blank}
```

## Build and Local Server

Local development — run the live server:

```bash
mkdocs serve
```

By default it listens on `http://127.0.0.1:8000`. Useful flags:

| Flag | Effect |
| --- | --- |
| `--dev-addr 0.0.0.0:9000` | Change address and port, useful in a container |
| `--strict` | Build fails on any warning, including broken links |
| `--livereload` | Page reloads in the browser without F5 (on by default) |
| `--no-livereload` | Disable auto-refresh |
| `--clean` | Remove `site/` before building |

Build the artifact for deployment:

```bash
mkdocs build --clean --strict
```

`--strict` is mandatory in CI. Otherwise typos in links and missing files in `nav` will be silently built. The exit code on warning is zero, so without `--strict` the pipeline passes green with broken documentation.

> [!WARNING]
> Do not run `mkdocs serve` in production. It is a dev server with no authentication and file reload enabled.

Common errors on first run:

- `WARNING - A relative path to '...' is included in the 'nav' config`. A file is listed in `nav` but missing from `docs/`. Check case and path.
- `WARNING - Documentation file 'x.md' is not included in the 'nav' configuration`. The file exists but is not in navigation. Either add it to `nav` or rely on auto-navigation by removing the `nav` section entirely.
- `ERROR - Config value 'theme': The theme 'mkdocs' is not installed`. The theme package is not installed, or the name is misspelled.

## Material for MkDocs: Navigation, Search, Tabs

Material extends base MkDocs with three things that are almost always needed.

**Navigation.** Enabled through `features` in the `theme` section:

```yaml
theme:
  name: material
  features:
    - navigation.tabs          # top-level tabs
    - navigation.sections      # tabs stick on scroll
    - navigation.top           # "back to top" button
    - navigation.indexes       # index.md becomes a section
    - navigation.tracking      # anchor in URL on navigation
    - toc.follow               # right-side TOC scrolls with text
```

> [!TIP]
> The combination of `navigation.tabs` + `navigation.sections` gives the familiar "sticky" menu. Without `sections` the tabs scroll away with the content.

**Search.** The `search` plugin ships with Material but registers separately:

```yaml
plugins:
  - search:
      separator: '[\s\-\.\_]+'
```

For Russian documentation, stemming is important — set `search.lang: ru` in the `theme` config. The list of supported languages is published in Material's documentation and new languages are added regularly.

**Tabs within a page.** Implemented via the `pymdownx.tabbed` extension, connected above. The alternative syntax using `!!! example` blocks does not work in some themes — this is a frequent reason "why tabs don't render."

**Code highlighting and copying.** The "copy" button is enabled by the `content.code.copy` feature. Line numbers come from the `pymdownx.highlight` extension with `linenums: true` or globally via `markdown_extensions`:

```yaml
markdown_extensions:
  - pymdownx.highlight:
      anchor_linenums: true
      line_spans: __span
      pygments_lang_class: true
  - pymdownx.inlinehilite
  - pymdownx.snippets
  - pymdownx.superfences
```

> [!WARNING]
> `pymdownx.superfences` is required for highlighting inside admonitions and tabbed blocks. Without it, code renders as a plain block without syntax highlighting.

Dark theme is a must-have for documentation read at night on-call. The toggle is configured in the example config above through `palette` with two schemes: `default` and `slate`. To make Material serve the correct scheme on first visit, add a custom script to `<head>` or use `theme.palette.toggle` with `media` — it works without JS and respects the user's system settings.
