π Installing MkDocs-Material¶

Material is a custom theme for the MkDocs static site generator that's geared towards building project documentation. Documentation source files are written in Markdown, and configured with a single YAML configuration file
Info
Both projects are open-source and can be downloaded here: https://github.com/squidfunk/mkdocs-material.
π₯ Installation¶
π Requirements¶
π³ Install MkDocs¶
The use of Docker Compose will automate the installation of MkDocs container.
π§ Setup MkDocs Parameters
Before deploying, you need to define a few environment variables that will be used throughout the setup process.
HOST_PORT: external port used by NGINX to route traffic to the serviceGIT_REPOS: path to the local git repository
# example of configuration for environment parameters
GIT_REPOS=/path/to/git-repos.git
HOST_PORT=10000
βοΈ Configure MkDocs for Docker Compose
MkDocs can be deployed using Docker Compose.
The compose.yml file will automatically incorporate the environment variables configured in the previous step.
You can copy, paste, and run all of the following commands directly in your terminal.
# create docker directory
mkdir mkdocs && cd mkdocs
mkdir build
# setup of compose.yml
tee compose.yml > /dev/null <<'EOF'
services:
mkdocs-docs:
build:
context: ./build
additional_contexts:
- repo=${GIT_REPOS}
args:
- PUID=${PUID}
- PGID=${PGID}
image: mkdocs-docs:latest
container_name: mkdocs-docs
user: "${PUID}:${PGID}"
volumes:
- ${GIT_REPOS}:/git:ro
ports:
- ${HOST_PORT}:8000
healthcheck:
test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://localhost:8000/"]
start_period: 180s
start_interval: 5s
interval: 60s
timeout: 1s
retries: 3
restart: unless-stopped
EOF
# setup of .env file
tee .env > /dev/null <<EOF
###################################################################################
# Run as non-root user
###################################################################################
PUID=`id -u`
PGID=`id -g`
###################################################################################
# NGINX Proxy Configuration
###################################################################################
HOST_PORT=${HOST_PORT}
###################################################################################
# MkDocs Configuration
###################################################################################
GIT_REPOS=${GIT_REPOS}
EOF
Keep the .env file
All the secret informations will be stored in the .env file.
π³ Create the custom MkDocs-Material docker container
To create a custom Docker container for Mkdocs-Material based on python, follow these steps:
-
Configure the
requirements.txtFile
Modify therequirements.txtfile to specify the required MkDocs plugins that will be installed in the Docker container.# setup requirements.txt file tee build/requirements.txt > /dev/null <<EOF mkdocs-material mkdocs-awesome-pages-plugin mkdocs-minify-plugin mkdocs-macros-plugin mkdocs-mermaid2-plugin EOF -
Create the
run.shScript
Develop arun.shscript that will be executed upon container startup.
This script should perform the following actions:- π Check if a Git repository needs to be cloned.
- β¬οΈ Fetch the latest commits from the Git repository.
- π§Ή Use
git reset --hardto navigate to the latest commits, handling cases where the commit history has been rewritten. - βοΈ Build the MkDocs-material documentation if necessary.
- π Launch a Python HTTP server to serve the static website created.
# create run.sh script tee build/run.sh > /dev/null <<'EOF' #!/bin/sh # Update + build only when the Git HEAD changed. # The build hash is stored in .env ONLY if the build succeeds. set -u # error on undefined variables ############################################# # Configuration ############################################# LOGS="/tmp/update-logs.log" RETRY_DELAY=60 ENV_FILE=".env" BRANCH="master" ############################################# # Helpers ############################################# log_step() { printf "=== %s: " "$1" } fail_with_logs() { echo "failed ===" echo "---- logs ----" cat "$LOGS" echo "--------------" echo "$1" sleep "$RETRY_DELAY" exit 1 } get_current_hash() { git rev-parse --short HEAD } get_previous_hash() { if [ -f "$ENV_FILE" ]; then grep -E '^LATEST_COMMIT=' "$ENV_FILE" 2>/dev/null | head -n1 | cut -d= -f2- else echo "" fi } store_hash() { new_hash="$1" if [ -f "$ENV_FILE" ] && grep -q '^LATEST_COMMIT=' "$ENV_FILE" 2>/dev/null; then sed -i "s|^LATEST_COMMIT=.*|LATEST_COMMIT=$new_hash|" "$ENV_FILE" else printf "\nLATEST_COMMIT=%s\n" "$new_hash" >> "$ENV_FILE" fi } run_cmd() { "$@" > "$LOGS" 2>&1 return $? } ############################################# # Main Script ############################################# # Update repository log_step "git pull" run_cmd git pull origin "$BRANCH" --rebase if [ $? -ne 0 ]; then fail_with_logs "git pull failed" fi echo "succeed ===" echo "" # Compare hashes current_hash="$(get_current_hash)" previous_hash="$(get_previous_hash)" if [ "$current_hash" != "$previous_hash" ]; then echo "hash change -> building" echo "" # Build only when hash changed log_step "building mkdocs-material documentation" rm -rf ./site run_cmd mkdocs build --no-directory-urls if [ $? -ne 0 ]; then fail_with_logs "build failed (hash NOT updated)" fi echo "succeed ===" echo "" # Add apple-touch-icon, android and manifest files if [ -f "./site/index.html" ]; then log_step "adding apple/android/manifest icons files" sed -i '/<link rel="icon" href="assets\/images\/favicon.png">/d' "./site/index.html" sed -i '20i\ <link rel="manifest" href="/app.webmanifest">\ <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">\ <link rel="icon" type="image/png" href="/favicon.png">\ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">\ ' ./site/index.html echo "succeed ===" echo "" fi # Store the hash ONLY if build succeeded store_hash "$current_hash" else echo "no hash change -> skipping build" echo "" fi # Launch python http server echo "running python http-server on kouizine site directory..." run_cmd python -m http.server 8000 -d ./site if [ $? -ne 0 ]; then fail_with_logs "python http-server failure" fi EOF -
Create the Dockerfile:
Create a Dockerfile that includes the following steps:- Copy the
requirements.txtfile into the Docker container. - Copy the
run.shscript into the Docker container.
# setup of Dockerfile tee build/Dockerfile > /dev/null <<'EOF' ############################################# ##### Builder stage: install Python deps ############################################# FROM python:3.12-slim AS builder # work dir for building Python dependencies WORKDIR /tmp # install required MkDocs plugins and extensions COPY requirements.txt /tmp/requirements.txt RUN set -eux; \ pip install --no-cache-dir --upgrade pip; \ pip install --no-cache-dir --prefix=/install -r /tmp/requirements.txt ############################################# ##### Final stage: runtime image ############################################# FROM python:3.12-slim # configuration variables ARG PUID=1000 ARG PGID=1000 # container paths ENV APP_DIR=/docs ENV RUN_SCRIPT=/usr/src/app/run.sh # install base runtime tools RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ git \ curl \ tini \ ; \ rm -rf /var/lib/apt/lists/* # copy installed Python packages from builder stage COPY --from=builder /install /usr/local # non-root user setup RUN set -eux; \ groupadd --gid "${PGID}" debian; \ useradd --uid "${PUID}" --gid "${PGID}" --shell /bin/bash --create-home debian; \ mkdir -p "${APP_DIR}"; \ chown -R debian:debian "${APP_DIR}"; \ chmod 2775 "${APP_DIR}" # runtime configuration USER debian WORKDIR ${APP_DIR} # clone directory from a local Git context COPY --from=repo / /git RUN set -eux; \ git config --global --add safe.directory /git; \ git clone /git . USER root RUN rm -rf /git USER debian # copy the runtime script that updates repos, builds docs, and starts the server COPY --chown=${PUID}:${PGID} run.sh ${RUN_SCRIPT} RUN chmod 0755 ${RUN_SCRIPT} # runtime configuration EXPOSE 8000 ENTRYPOINT ["/usr/bin/tini", "--", "/usr/src/app/run.sh"] EOF - Copy the
π³ Install MkDocs with Docker Compose
Now that the compose.yml file has been generated, it's time to install all the containers.
# install and start the container
docker compose up --build -d
π Deploy MkDocs¶
Install NGINX
NGINX needs to be installed, follow the NGINX section.
Configure NGINX
NGINX needs to be configured using a file in /etc/nginx/sites-enabled directory.
This configuration file specify the documentation path:
server {
server_name mkdocs.domain.fr;
# setup 404 error_page
error_page 404 /404.html;
include snippets/error-404.conf;
# reverse proxy
location / {
proxy_pass http://127.0.0.1:10000;
# keep it HTTP/1.1
proxy_http_version 1.1;
# forwarded headers
include proxy_params;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# timeouts for long-running sessions
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
# restart nginx
sudo nginx -t && sudo service nginx restart
Replace
mkdocs.domain.frby the name of your website.
Activate HTTPS
To activate HTTPS protocol, follow theΒ Let's Encrypt section.
πͺ Create Git hook script¶
The following git hook script automatically generates the MkDocs static site whenever a git push is made.
# create post-update hook in git repository
tee post-update > /dev/null <<EOF
echo "=== restarting docker container: mkdocs-docs ==="
docker restart mkdocs-docs
echo "=== done ===\n"
exit 0
EOF
Add the git user to docker group
To enable the ability to restart the mkdocs-docs container using the docker command, it is essential for the post-update script to access the file located at /var/run/docker.sock.
This necessitates the inclusion of the user git to the docker group.
# add git user to the docker group
sudo usermod -aG docker git