this describes how i take my zk directory at ~/www/zk,
which i manage with my zk script
(TODO: write and link page for this)
and publish it here on this website.
this description is incomplete because i have yet to share my
zk2html.py or zk2wsgi.py scripts.
the core functionalities are described at a high level.
it is using:
i've written a zk2html.py script which is responsible for taking the source files and converting them into html files.
this handles:
i've also written a zk2wsgi.py script which can serve the built files (although nginx precludes this for the most part),
but is primarily responsible for generating http 302 redirects to pages when the id matches but the rest of the page uri does not.
this allows me change the title and keywords on any page without breaking existing links.
Dockerfile for the scriptsFROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY zk2html.py zk2wsgi.py ./
docker-compose.yaml fileservices:
builder:
build: .
command: python zk2html.py watch -s /source -d /build
volumes:
- /srv/zk/src:/source:ro
- ./build:/build
restart: unless-stopped
healthcheck:
# _id_map.json is written last by zk2html, so its presence means the build is complete
test: ["CMD", "test", "-f", "/build/_id_map.json"]
interval: 5s
timeout: 3s
retries: 24
start_period: 10s
server:
build: .
command: gunicorn -w 2 -b 0.0.0.0:8000 'zk2wsgi:create_app()'
volumes:
- /srv/zk/src:/source:ro
- ./build:/build:ro
ports:
- "8000:8000"
depends_on:
builder:
condition: service_healthy
environment:
ZK_SOURCE_DIR: /source
ZK_BUILD_DIR: /build
restart: unless-stopped
the build directory is served directly by nginx, source files are served so the various non-markdown files can be accessed, and also so the markdown source files can be viewed.
the @zk location which points to the above zk-server container handles http 302 redirects when an id matches as existing page
but the rest of the name doesn't and needs to be rewritten.
server {
location / {
root /srv/zk/build;
index index.html;
try_files $uri $uri.html $uri/ @source;
}
location @source {
root /srv/zk/src;
try_files $uri @zk;
}
location @zk {
proxy_pass http://localhost:8000;
}
}
using unison to automatically (if there's no conflicts) sync my zk source file to the directory where they are build and served by my webserver. files changing in the source directory on my webserver triggers an automatic site rebuild.
my unison config ~/unison/zk.prf:
root=/home/slime/www/zk
root=ssh://jinro//srv/zk/src
times = true
auto = true
batch = true
xferbycopying = true
fastcheck = true
example run:
Unison 2.53.8 (ocaml 5.4.0): Contacting server...
Connected [//jinro//srv/zk/src -> //mithril//home/slime/www/zk]
Looking for changes
Waiting for changes from server
new file ----> z6liqdr--how-this-website-is-built-and-published__meta_zk.md
deleted ----> z6lllm7==draft--how-i-build-and-publish-this-website__meta.md
Reconciling changes
99% 1/2 (2.0 KiB of 2.0 KiB) --:-- ETA
2 items will be synced, 0 skipped
2.0 KiB to be synced from local to jinro
0 B to be synced from jinro to local
Propagating updates
Unison 2.53.8 (ocaml 5.4.0) started propagating changes at 13:45:33.17 on 03 May 2026
[BGN] Copying z6liqdr--how-this-website-is-built-and-published__meta_zk.md from /home/slime/www/zk to //jinro//srv/zk/src
[END] Copying z6liqdr--how-this-website-is-built-and-published__meta_zk.md
[BGN] Deleting z6lllm7==draft--how-i-build-and-publish-this-website__meta.md from //jinro//srv/zk/src
[END] Deleting z6lllm7==draft--how-i-build-and-publish-this-website__meta.md
Unison 2.53.8 (ocaml 5.4.0) finished propagating changes at 13:45:33.17 on 03 May 2026, 0.002 s
Saving synchronizer state
Synchronization complete at 13:45:33 (2 items transferred, 0 skipped, 0 failed)