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:

[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¶
- First request: Markdown rendered to HTML, cached
- Subsequent requests: Served from cache (instant)
- 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:
- Set
DEBUG=false - Configure
BASE_URLfor llms.txt - Use proper
DOCS_ROOTpath - Set up cache directory with write permissions
- Configure reverse proxy (nginx/caddy)
- Enable HTTPS
- Set proper PORT if needed
- Add health check monitoring
- Configure log aggregation
- Set up backup for docs
- For k8s/k3s: Configure emptyDir volume for cache
- For k8s/k3s: Consider per-pod rate limit scaling
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¶
- Deployment Guide - Deploy to production
- API Reference - HTTP endpoints
- Examples - Configuration examples
-
Like this footnote! ↩