How to build a documentation portal with MkDocs and GitLab Pages
I wrote detailed documentation for the popular openvpn-install script — covering everything from quick start to script internals. Here's how I built the documentation portal using MkDocs Material, GitLab Pages, and CI/CD with pinned dependencies.
The result is a fast, versioned, searchable documentation site with dark mode, code highlighting, Mermaid diagrams, and zero backend. Every push to main triggers an automatic build and deploy.
What the documentation covers
The docs are structured into logical sections: installation and quick start, client and server management, CLI reference, network/DNS/firewall configuration, security and encryption settings, advanced topics like fingerprint authentication and Data Channel Offload, and a deep dive into every part of the script's internals — from OS detection to the interactive menu system.
Directory structure
├── docker-compose.yml
├── .gitlab-ci.yml
├── mkdocs.yml
├── overrides/
└── docs/
├── index.md
├── compatibility.md
├── installation.md
├── quickstart.md
├── testing.md
├── faq.md
├── css/
│ └── extra.css
├── media/
│ └── logo/
│ ├── openvpn.svg
│ └── openvpn-favicon.svg
├── usage/
│ ├── client-management.md
│ ├── server-management.md
│ └── uninstall.md
├── configuration/
│ ├── cli-reference.md
│ ├── network-dns.md
│ ├── security.md
│ └── firewall.md
├── advanced/
│ ├── fingerprint-auth.md
│ ├── dco.md
│ └── customization.md
└── script-internals/
├── overview.md
├── logging.md
├── validation.md
├── os-detection.md
├── network-detection.md
├── installation-process.md
├── client-operations.md
├── server-operations.md
└── interactive-menu.md
mkdocs.yml — key parts
The full config uses Material theme with custom branding, light/dark mode toggle, sticky navigation tabs, instant loading, and a rich set of Markdown extensions including Mermaid diagram support.
site_name: OpenVPN Install Docs
nav:
- "Overview":
- "Introduction": index.md
- "Compatibility": compatibility.md
- "Getting Started":
- "Installation": installation.md
- "Quick Start": quickstart.md
- "Usage":
- "Client Management": usage/client-management.md
- "Server Management": usage/server-management.md
- "Uninstall": usage/uninstall.md
- "Configuration":
- "CLI Reference": configuration/cli-reference.md
- "Network & DNS": configuration/network-dns.md
- "Security & Encryption": configuration/security.md
- "Firewall": configuration/firewall.md
- "Advanced":
- "Fingerprint Auth": advanced/fingerprint-auth.md
- "Data Channel Offload": advanced/dco.md
- "Customization": advanced/customization.md
- "Script Internals":
- "Overview": script-internals/overview.md
- "Logging System": script-internals/logging.md
- "Validation & Parsing": script-internals/validation.md
- "OS Detection": script-internals/os-detection.md
- "Network Detection": script-internals/network-detection.md
- "Installation Process": script-internals/installation-process.md
- "Client Operations": script-internals/client-operations.md
- "Server Operations": script-internals/server-operations.md
- "Interactive Menu": script-internals/interactive-menu.md
- "Testing": testing.md
- "FAQ": faq.md
theme:
name: material
custom_dir: overrides
logo: media/logo/openvpn.svg
favicon: media/logo/openvpn-favicon.svg
features:
- navigation.instant
- navigation.instant.prefetch
- navigation.tabs
- navigation.tabs.sticky
- navigation.sections
- navigation.top
- search.suggest
- search.highlight
- content.code.copy
- content.code.select
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
toggle:
icon: material/weather-night
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
toggle:
icon: material/weather-sunny
name: Switch to light mode
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.tabbed:
alternate_style: true
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- toc:
permalink: true
plugins:
- search
- minify:
minify_html: true
.gitlab-ci.yml
All Python dependencies are pinned to exact versions for reproducible builds. The pipeline uses rules instead of the deprecated only keyword, and artifacts expire after 1 hour to save storage.
image: python:3.13
stages:
- deploy:static
pages:
stage: deploy:static
script:
- pip install mkdocs==1.6.1
- pip install mkdocs-material==9.7.6
- pip install mkdocs-material-extensions==1.3.1
- pip install mkdocs-minify-plugin==0.8.0
- pip install mkdocs-redirects==1.2.2
- mkdocs build --site-dir public
artifacts:
expire_in: 1 hrs
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
docker-compose.yml — local preview
For local development, the same pinned dependencies are installed inside a Python container. Run docker compose up and open http://localhost:8000 to preview with live reload.
services:
mkdocs:
image: python:3.13
volumes:
- .:/docs
working_dir: /docs
ports:
- "8000:8000"
command: >
bash -c "pip install
mkdocs==1.6.1
mkdocs-material==9.7.6
mkdocs-material-extensions==1.3.1
mkdocs-minify-plugin==0.8.0
mkdocs-redirects==1.2.2
&& mkdocs serve -a 0.0.0.0:8000"
Result
Push to main, wait for the pipeline, and the site is live. No manual deploys, no servers to maintain.
- Documentation site: alan-lt.gitlab.io/openvpn-install-docs
- Source repository: gitlab.com/alan-lt/openvpn-install-docs
Comments
Post a Comment