Configuration

Configure the documentation server with environment variables and file structure.

Environment Variables

Core Settings

Variable Default Description
DOCS_ROOT ./test_docs or /app/docs Root directory for markdown files
CACHE_ROOT ./__cache__ or /app/cache Cache directory for rendered HTML
PORT 8080 HTTP server port
DEBUG false Enable debug mode with auto-reload
BASE_URL Auto-detected Base URL for absolute links in llms.txt

MCP Settings

Variable Default Description
MCP_ENABLED true Enable/disable MCP endpoint
MCP_RATE_LIMIT_REQUESTS 120 Max requests per window per IP
MCP_RATE_LIMIT_WINDOW 60 Rate limit window in seconds
MCP_MAX_SEARCH_RESULTS 10 Default max search results
MCP_SNIPPET_LENGTH 200 Max characters for search snippets

Example Configurations

Development:

DOCS_ROOT=./docs \
DEBUG=true \
PORT=3000 \
uv run python -m docs_server

Production:

DOCS_ROOT=/var/www/docs \
CACHE_ROOT=/var/cache/docs \
BASE_URL=https://docs.example.com \
PORT=8080 \
uv run python -m docs_server

Docker:

docker run \
  -e DOCS_ROOT=/app/docs \
  -e BASE_URL=https://docs.mysite.com \
  -e DEBUG=false \
  -p 8080:8080 \
  -v $(pwd)/docs:/app/docs \
  markdown-docs-server


Required File Structure

Your DOCS_ROOT must contain three required files:

docs/
├── index.md       # Homepage (REQUIRED)
├── sidebar.md     # Sidebar navigation (REQUIRED)
└── topbar.md      # Top bar navigation (REQUIRED)

index.md

Your documentation homepage:

# Welcome to My Project

Quick introduction and getting started guide.

## Features
- Feature 1
- Feature 2

## Quick Start
[Get started here](quick-start.html)

sidebar.md

Hierarchical navigation structure:

# Navigation

* [Home](index.html)
* [Getting Started](getting-started.html)
* [User Guide](user-guide.html)
  * [Installation](user-guide/install.html)
  * [Configuration](user-guide/config.html)
* [API Reference](api.html)
  * [REST API](api/rest.html)
  * [GraphQL](api/graphql.html)

Rules: - Top-level items: * [Title](link.html) - Child items: * [Title](link.html) (2 spaces indent) - Sections automatically detected - .md extensions converted to .html in rendered output

topbar.md

Top bar with left/middle/right sections:

# Top Bar Navigation

## left
* [📚 Docs](index.html)
* [🚀 Quick Start](quick-start.html)

## middle
* [v2.0.0](https://github.com/project/releases)

## right
* [GitHub](https://github.com/project)
* [Discord](https://discord.gg/project)

Sections: - ## left - Left side (logo, main links) - ## middle - Center (optional, breadcrumbs) - ## right - Right side (external links, social)


Optional Files

llms.txt

AI assistant index (see LLMs.txt Guide):

# Project Documentation

[Homepage](index.html)
[Quick Start](quick-start.html)
[API Reference](api.html)

If not provided, auto-generated from sidebar.md + index.md.

assets/

Place static assets in an assets/ folder:

docs/
└── assets/
    ├── logo.svg
    ├── screenshot.png
    └── diagram.pdf

Reference in markdown:

![Logo](assets/logo.svg)
[Download PDF](assets/diagram.pdf)


Directory Structure

Organize content in subdirectories:

docs/
├── index.md
├── sidebar.md
├── topbar.md
├── getting-started.md
├── user-guide/
│   ├── installation.md
│   ├── configuration.md
│   └── troubleshooting.md
├── api/
│   ├── rest.md
│   ├── graphql.md
│   └── webhooks.md
├── deployment/
│   ├── docker.md
│   ├── kubernetes.md
│   └── cloud.md
└── assets/
    ├── logo.svg
    └── images/
        └── screenshot.png

Best practices: - Group related content in folders - Use clear, URL-friendly names - Keep hierarchy shallow (2-3 levels max) - Use descriptive filenames


Markdown Configuration

The server uses these Markdown extensions:

Enabled Extensions

Extension Purpose
codehilite Syntax highlighting for code blocks
toc Automatic table of contents
tables GitHub-style tables
fenced_code ``` code blocks
footnotes Footnote support1
attr_list HTML attributes in markdown
def_list Definition lists
abbr Abbreviations
pymdownx.superfences Advanced code blocks
pymdownx.tasklist - [ ] Task lists
pymdownx.highlight Enhanced highlighting
pymdownx.inlinehilite Inline code highlight

Extension Configuration

Customize in config.py:

markdown_extension_configs = {
    "codehilite": {
        "css_class": "highlight",
        "use_pygments": True,
    },
    "toc": {
        "permalink": True,
        "toc_depth": 3,
        "permalink_title": "🔗",
    },
    "pymdownx.tasklist": {
        "custom_checkbox": True,
    },
}

Caching Strategy

How It Works

  1. First request: Markdown rendered to HTML, cached
  2. Subsequent requests: Served from cache (instant)
  3. Server restart: Cache cleared automatically

Cache Location

# Default cache directory
CACHE_ROOT=./__cache__

# Custom cache directory
CACHE_ROOT=/tmp/docs-cache

Cache Files

__cache__/
├── index.html               # Cached homepage
├── quick-start.html         # Cached page
├── user-guide/
│   └── installation.html
├── llms.txt                 # Cached llms.txt
└── llms-full.txt           # Cached full content

Manual Cache Clear

# Remove cache directory
rm -rf __cache__

# Server will recreate on next request

Security Settings

Path Traversal Protection

Built-in security prevents directory traversal:

# These are blocked:
/../../../../etc/passwd  
/../secret.md            
/../../config.yml        

# These work:
/user-guide/install.md   
/api/endpoints.md        

Safe Path Validation

All file paths are validated before serving:

def is_safe_path(path: str, base_path: Path) -> bool:
    """Validate path is within allowed directory."""
    abs_base = base_path.resolve()
    abs_path = (base_path / path).resolve()
    return os.path.commonpath([abs_base, abs_path]) == str(abs_base)

URL Routing

HTML Mode

Serves rendered HTML:

http://localhost:8080/index.html          → Rendered
http://localhost:8080/api/endpoints.html  → Rendered

Raw Markdown Mode

Serves original markdown:

http://localhost:8080/index.md            → Raw markdown
http://localhost:8080/api/endpoints.md    → Raw markdown

Static Assets

Direct file serving:

http://localhost:8080/assets/logo.svg     → Static file
http://localhost:8080/assets/doc.pdf      → Static file

Special Endpoints

http://localhost:8080/                    → Redirects to /index.html
http://localhost:8080/health              → Health check JSON
http://localhost:8080/llms.txt            → AI assistant index
http://localhost:8080/llms-full.txt       → Full documentation

MCP Cache Considerations

Kubernetes / k3s Deployments

When deploying to Kubernetes or k3s with MCP enabled:

Cache Persistence: - MCP search index is stored in CACHE_ROOT/mcp/ - Each pod builds its own index on first startup (~200ms for typical docs) - Cache persists across container restarts if volume is mounted - Recommended: Use emptyDir volume for cache (ephemeral, fast)

# Example volume mount for cache
volumes:
  - name: cache
    emptyDir: {}
volumeMounts:
  - name: cache
    mountPath: /app/cache

Horizontal Scaling: - Each pod maintains its own independent search index - Index rebuilds automatically when docs change (hash-based validation) - No shared state required between pods - Rate limiting is per-pod (each pod has 120 req/min limit)

Startup Performance: - First startup: ~200ms to build index (100 docs) - Subsequent startups: ~10ms to load from cache - Health check passes immediately (MCP initialization is async)

Environment Variables:

env:
  - name: MCP_ENABLED
    value: "true"
  - name: MCP_RATE_LIMIT_REQUESTS
    value: "120"
  - name: CACHE_ROOT
    value: "/app/cache"

Production Checklist

Before deploying to production:


Troubleshooting

Server won't start

Check port availability:

lsof -i :8080  # See what's using port 8080

Files not found

Verify DOCS_ROOT:

echo $DOCS_ROOT
ls $DOCS_ROOT  # Should show index.md, sidebar.md, topbar.md

Styling broken

Check for required files:

ls $DOCS_ROOT/sidebar.md
ls $DOCS_ROOT/topbar.md

Cache issues

Clear and restart:

rm -rf __cache__
uv run python -m docs_server


Advanced Configuration

Custom Port

PORT=3000 uv run python -m docs_server

Multiple Environments

# Development
cp .env.development .env
uv run python -m docs_server

# Production
cp .env.production .env
uv run python -m docs_server

Environment File

Create .env:

DOCS_ROOT=./docs
CACHE_ROOT=./__cache__
PORT=8080
DEBUG=false
BASE_URL=https://docs.mysite.com

Load with:

source .env && uv run python -m docs_server


Next Steps


  1. Like this footnote!