Skip to content

Local MkDocs Setup & Workflow

This document outlines the local installation, configuration, and daily workflow to set up MkDocs for the sfhomelab documentation site.

The site is built using MkDocs with the Material for MkDocs theme.

Local Environment Initialization

To keep the system clean and avoid conflicting Python packages, MkDocs is running inside an isolated virtual environment (venv).

Initial Setup (Run once per machine):

# Navigate to the docs root folder/repository
cd /mnt/pool01/homelab/docs-sfhomelab

# Create fresh Python virtual environment
python -m venv venv

# Activate the environment
source venv/bin/activate

# Install the core engine and theme
pip install mkdocs-material

Note : The venv/ folder which is auto-created contains thousands of local system binaries and must never be committed to Git. The same applies to the dynamically generated site/ folder. Both are explicitly blocked in the .gitignore file.

Directory Architecture

All core Markdown files live within specific domain folders, while global assets and snippets are centralized.

docs-sfhomelab/
├── docs/
│   ├── 01-infra/          # Host OS, Networking, Storage, Firewall
│   ├── 02-gateway/        # Caddy, Crowdsec, Authentik, Tailscale
│   ├── 03-media/          # Jellyfin, Arr Stack, Profilarr
│   ├── 04-utilities/      # Monitoring, Backups, Maintenance Scripts
│   ├── snippets/          # Dynamically embedded code (configs, scripts)
│   ├── stylesheets/       # Custom CSS overrides (extra.css)
│   ├── assets/            # Static media
│   └── index.md           # Landing file
├── mkdocs.yml             # The master config file
└── .gitignore             # Blocking venv/ and site/

Configuration Highlights (mkdocs.yml)

The mkdocs.yml file dictates the entire site structure, theme, and markdown extensions.

Expand to view current mkdocs.yml
site_name: SF Homelab
site_url: https://docs.sfhomelab.com
repo_url: https://github.com/sheikhfarhan/docs-sfhomelab
edit_uri: edit/main/docs/

theme:
name: material
font:
    text: Fira Sans
    code: Roboto Mono
logo: assets/logo02.png
favicon: assets/favicon.ico
features:
    - navigation.tabs
    - navigation.tabs.sticky
    - navigation.expand
    - navigation.path
    - navigation.sections
    - navigation.top
    - content.code.copy
    - navigation.footer      # The Next/Prev arrows
palette: 
    # Dark Mode
    - scheme: slate
    toggle:
        icon: material/weather-sunny
        name: Dark mode
    primary: indigo
    accent: deep purple

    # Light Mode
    - scheme: default
    toggle:
        icon: material/weather-night
        name: Light mode
    primary: blue
    accent: deep orange


markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences
- attr_list
- pymdownx.emoji
- tables
- pymdownx.snippets:
        base_path: docs
        check_paths: true
- pymdownx.inlinehilite

nav:
- Home: index.md

- Infra:
    - Deployment Guide: 01-infra/deployment-guide.md
    - Cloudflare: 01-infra/cloudflare-setup.md
    - Networking: 01-infra/networking.md
    - Firewall: 01-infra/security-firewall.md
    - Storage: 01-infra/storage.md
    - Backups: 01-infra/backups.md
    - Troubleshooting: 01-infra/troubleshooting.md

- Gateway:
    - Gateway Overview: 02-gateway/index.md
    - Caddy: 02-gateway/caddy.md
    - CrowdSec: 02-gateway/crowdsec.md
    - VoidAuth: 02-gateway/voidauth.md
    - Authentik: 02-gateway/authentik.md
    - Tailscale: 02-gateway/tailscale.md

- Media:
    - Media Overview: 03-media/index.md
    - Jellyfin-Seerr: 03-media/jellyfin-stack.md
    - Arr Stack: 03-media/vpn-arr-automation-stack.md
    - Anime x Profilarr: 03-media/profilarr-anime.md
    - Lifecycle of a Request: 03-media/bonus-info-story-mode.md

- Utilities:
    - Utilities Overview: 04-utilities/index.md
    # The "Monitoring" (Metrics & Logs)
    - Monitoring Stack:
        - Monitoring Overview: 04-utilities/monitoring-stack.md
        - Beszel: 04-utilities/beszel-setup.md

    # The "Maintenance" stuff (Backups & Alerts)
    - Notifications, Backups & Logs:
        - Gotify: 04-utilities/gotify.md
        - Kopia: 04-utilities/kopia.md
        - GoAccess: 04-utilities/goaccess.md

    - Scripts:
        - Pull All Images: 04-utilities/pull-all.md
        - Recreate All Services: 04-utilities/recreate-all.md

extra_css:
- stylesheets/extra.css
  • Branding: The site uses a custom logo and favicon, the images are stored in the assets folder.

  • Theme Features: Sticky navigation tabs, light/dark mode toggles, and code copy buttons are enabled globally.

The Snippets Engine

To embed code block templates natively, the pymdownx.snippets extension is configured with strict path checking:

  - pymdownx.snippets:
        base_path: docs
        check_paths: true
  • base_path: docs ensures snippet links can be cleanly formatted as snippets/file.txt without needing long relative paths.
  • check_paths: true acts as a fail-safe, forcing MkDocs to throw a loud terminal error if a snippet file is missing, rather than silently rendering an empty box.

The Snippet Sync Workflow

Because the actual /live files in the snippets eg: the scripts and the .env.example are in a separate repository/folder(services-sfhomelab), they must be synced over to the docs-sfhomelab repository after any updates or changes.

The idea is then to use a simple basic bash script (/mnt/pool01/homelab/sync-docs.sh) to copy into the snippets/ folder. This script will be at the root-level of the different repos/folders within our homelab architecture setup.

Expand to view the script
```sh

#!/bin/bash
# sfhomelab - Sync live infrastructure configs to documentation snippets

echo "🔄 Syncing live files to documentation snippets..."

# Define base paths
SERVICES_DIR="/mnt/pool01/homelab/services"
SNIPPETS_DIR="/mnt/pool01/homelab/docs-sfhomelab/docs/snippets"

# Ensure the snippets directory exists just in case
mkdir -p "$SNIPPETS_DIR"

# 1. Sync Automation Scripts (Saved as .txt for clean MkDocs rendering)
cp "$SERVICES_DIR/scripts/pull-all.sh" "$SNIPPETS_DIR/pull-all.txt"
cp "$SERVICES_DIR/scripts/recreate-all.sh" "$SNIPPETS_DIR/recreate-all.txt"

# 2. Sync Config Files (Dropping the hidden dots so MkDocs includes them)
cp "$SERVICES_DIR/.env.example" "$SNIPPETS_DIR/env.example.txt"
cp "$SERVICES_DIR/.kopiaignore" "$SNIPPETS_DIR/kopiaignore.txt"

echo "✅ Sync complete! Snippets are up to date."

```

Hidden Dot Rule

Linux hidden files (like .env.example or .kopiaignore) are automatically ignored by MkDocs security rules and will not be published. The sync script above handles this by dropping the leading dot when copying the files into the snippets directory (e.g., renaming .env.example to env-example.txt).

Daily Writing Workflow

When updating, I would follow this standard sequence:

# 1. Sync the latest scripts and configs from the services vault
/mnt/pool01/homelab/sync-docs.sh

# 2. Navigate to the docs repo and activate the environment
cd /mnt/pool01/homelab/docs-sfhomelab
source venv/bin/activate

# 3. Launch the live-reloading preview server
mkdocs serve --livereload