Files
docs/deployments/automation/Documentation/Zensical.md
T
nicole 289769a601
Automatic Documentation Deployment / Sync Docs to https://kb.bunny-lab.io (push) Successful in 8s
Restructured Documentation
2026-09-05 14:08:43 -06:00

233 lines
8.0 KiB
Markdown

---
tags:
- Zensical
- Documentation
---
## Purpose
After many years of using Material for MKDocs and it being updated with new features and security updates, it finally reached EOL around the end of 2025. The project maintainers started pivoting to a new successor called [Zensical](https://zensical.org/docs/get-started/). This document outlines my particular process for setting up a standalone documentation server within a virtual machine.
!!! info "Assumptions"
It is assumed that you are deploying this server into `Ubuntu Server 24.04.2 LTS (Minimal)`. It is also assumed that you are running every command as a user with superuser privileges (e.g. `root`).
You are generally safe to have a GuestVM with 16GB for the virtual disk, and expand it over-time based on your needs. CPU count and RAM allocation can also be extremely low based on your preferences, since this is simply a static page website at the end of the day.
## Architectural Overview
It is useful to understand the flow of data and how everything inter-connects, so I have provided a sequence diagram that you can follow below:
```mermaid
sequenceDiagram
autonumber
actor Author as Doc Author
participant Gitea as Gitea (Repo + Actions)
participant Runner as Act Runner
participant Zensical as Zensical Server (watch + build)
participant NGINX as NGINX (serves static site)
Author->>Gitea: Push to main
Gitea-->>Runner: Trigger workflow job
Runner->>Zensical: rsync docs → /srv/zensical/docs
Zensical-->>Zensical: Watch detects change
Zensical->>Zensical: Rebuild site → /srv/zensical/site
NGINX-->>NGINX: Serve files from /srv/zensical/site
```
## Setup Python Environment
The first thing we need to do is install the necessary python packages and install the zensical software stack inside of it.
```sh
sudo apt update && sudo apt upgrade -y
sudo apt install -y nano python3 python3.12-venv
mkdir -p /srv/zensical
cd /srv/zensical
python3 -m venv .venv
source .venv/bin/activate
pip install zensical
zensical new .
deactivate
# Remove Placeholder Example Docs
rm -rf /srv/zensical/docs/{*,.*}
```
## Zensical
### Configure Settings
Now we want to set some sensible defaults for Zensical to style it to look as close to Material for MKDocs as possible.
```sh
sudo tee /srv/zensical/zensical.toml > /dev/null <<'EOF'
[project]
site_name = "Bunny Lab"
site_description = "Server, Script, Workflow, and Networking Documentation"
site_author = "Nicole Rappe"
site_url = "https://kb.bunny-lab.io/"
repo_url = "https://git.bunny-lab.io/bunny-lab/docs"
repo_name = "bunny-lab/docs"
edit_uri = "_edit/main/"
[project.theme]
variant = "classic"
language = "en"
features = [
"announce.dismiss",
"content.action.edit",
"content.code.annotate",
"content.code.copy",
"content.code.select",
"content.footnote.tooltips",
"content.tabs.link",
"content.tooltips",
"navigation.indexes",
"navigation.instant",
"navigation.instant.prefetch",
"navigation.instant.progress",
"navigation.path",
"navigation.tabs",
"navigation.tabs.sticky",
"navigation.top",
"navigation.tracking",
"search.highlight",
]
[[project.theme.palette]]
scheme = "default"
toggle.icon = "lucide/sun"
toggle.name = "Switch to dark mode"
[[project.theme.palette]]
scheme = "slate"
toggle.icon = "lucide/moon"
toggle.name = "Switch to light mode"
EOF
```
### Create Watchdog Service
Since NGINX has taken over hosting the webpages, this does not need to be accessible from other servers, only NGINX itself which runs on the same host as Zensical. We only want to use the `zensical serve` command to keep a watchdog on the documentation folder and automatically rebuild the static site content when changes are detected. These changes are then served by NGINX's webserver.
```sh
# Create Service User, Assign Access, and Lockdown Zensical Data
sudo useradd --system --home /srv/zensical --shell /usr/sbin/nologin zensical || true
sudo chown -R zensical:zensical /srv/zensical
sudo find /srv/zensical -type d -exec chmod 2775 {} \;
sudo find /srv/zensical -type f -exec chmod 664 {} \; # This step likes to take a while, sometimes up to a minute.
sudo chmod 755 /srv/zensical/.venv/bin/* # Ensure Python Environment Executables Function
```
```sh
# Make Zensical Binary Executable for Service
sudo chmod +x /srv/zensical/.venv/bin/zensical
# Add Additional User(s) to Folder for Extra Access (Such as Doc Runners)
sudo usermod -aG zensical nicole
# Create Service
sudo tee /etc/systemd/system/zensical-watchdog.service > /dev/null <<'EOF'
[Unit]
Description=Zensical Document Changes Watchdog (zensical serve)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=zensical
Group=zensical
WorkingDirectory=/srv/zensical
# Run the venv binary directly; no activation needed
ExecStart=/srv/zensical/.venv/bin/zensical serve
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
EOF
# Start & Enable Automatic Startup of Service
sudo systemctl daemon-reload
sudo systemctl enable --now zensical-watchdog
```
### Updating
You will obviously want to keep Zensical up-to-date. You can run the following commands to upgrade it. This is taken and simplified from the original [Upgrade Documentation](https://zensical.org/docs/upgrade/) on Zensical's website.
```sh
# Upgrade Zensical
systemctl stop zensical-watchdog
cd /srv/zensical
source .venv/bin/activate
pip install --upgrade --force-reinstall zensical
deactivate
systemctl start zensical-watchdog
```
## NGINX Webserver
We need to deploy NGINX as a webserver, because when using reverse proxies like Traefik, it seems to not get along with Zensical at all. Attempts to resolve this all failed, so putting the statically-built copies of site data that Zensical generates into NGINX's root directory is the second-best solution I came up with. Traefik can be reasonably expected to behave when interacting with NGINX versus Zensical's built-in webserver.
```sh
sudo apt install -y nginx
sudo rm -f /etc/nginx/sites-enabled/default
sudo tee /etc/nginx/sites-available/zensical.conf > /dev/null <<'EOF'
server {
listen 80;
listen [::]:80;
server_name _;
root /srv/zensical/site;
index index.html;
# Primary document handling
location / {
try_files $uri $uri/ /index.html;
}
# Static asset caching (safe for docs)
location ~* \.(css|js|png|jpg|jpeg|gif|svg|ico|woff2?)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800, immutable";
try_files $uri =404;
}
# Prevent access to source or metadata
location ~* \.(toml|md)$ {
deny all;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/zensical.conf /etc/nginx/sites-enabled/zensical.conf
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl enable nginx
```
## Configure Documentation Delivery
Continue with [Publish Zensical Documentation with a Host Runner](<../../../workflows/Automation/Gitea/Publish Zensical Documentation with a Host Runner.md>) to register the runner and configure repository synchronization. Return here to configure the reverse proxy.
## Traefik Reverse Proxy
It is assumed that you use a [Traefik](<../../Networking and Access/Reverse Proxies/Traefik.md>) reverse proxy and are configured to use [dynamic configuration files](<../../Networking and Access/Reverse Proxies/Traefik.md#dynamic-configuration-files>). Add the file below to expose the Zensical service to the rest of the world.
```yaml title="kb.bunny-lab.io.yml"
http:
routers:
kb:
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: kb
rule: Host(`kb.bunny-lab.io`)
services:
kb:
loadBalancer:
servers:
- url: http://192.168.3.8:80
passHostHeader: true
```
## Related Documentation
- [Related Automation Documentation](<../../../reference/Automation/index.md>) — Find the connected deployments, procedures, and references for this subject.
- [Traefik Deployment](<../../Networking and Access/Reverse Proxies/Traefik.md>) — Prepare the reverse proxy before applying this page's routing configuration.