Skip to content

Documentation static service

Lifecycle: Current. Last verified: 2026-07-17.

Service record

Field Value
Purpose Serve generated MkDocs files to ingress Caddy
Owner IT Operations
Criticality / recovery priority High / P3
Host/runtime CT 101, rootless Podman as podsvc
Image docker.io/library/caddy:2-alpine
Unit docs-static.service
Quadlet /home/podsvc/.config/containers/systemd/docs-static.container
Data /opt/podman/volumes/forgejo/runner-data/publish/kh3-it-infra-docs/site
Mount Data path to /srv:ro
Listener CT host 30084 to container 8080
Public route docs.kh3group.com through CT 103

Architecture and dependencies

Forgejo Actions owns the build and copy. This container owns only static serving. It does not mount Git source, run MkDocs, or write the artifact. It depends on CT 101, the publish directory, rootless user systemd, and Podman. Public access additionally depends on CT 103, DNS, and OPNsense.

Reconstruction

Create the publish directory as podsvc, restore or rebuild the artifact, and install this Quadlet:

[Unit]
Description=KH3 MkDocs static site rootless container
After=network-online.target
Wants=network-online.target

[Container]
Image=docker.io/library/caddy:2-alpine
ContainerName=docs-static
Network=kh3-backend.network
Volume=/opt/podman/volumes/forgejo/runner-data/publish/kh3-it-infra-docs/site:/srv:ro
Exec=caddy file-server --root /srv --listen :8080
PublishPort=30084:8080
AutoUpdate=registry

[Install]
WantedBy=default.target

No secret is required by this service.

Operate

ssh pvessh 'pct exec 101 -- runuser -l podsvc -c \
  "XDG_RUNTIME_DIR=/run/user/2000 systemctl --user status docs-static.service --no-pager"'
ssh pvessh 'pct exec 101 -- runuser -l podsvc -c \
  "podman logs --tail=100 docs-static"'
ssh pvessh 'pct exec 101 -- curl -sS -o /dev/null -w "%{http_code}\n" \
  http://127.0.0.1:30084/'

Expected: active unit and 200.

After an approved Quadlet change:

ssh pvessh 'pct exec 101 -- runuser -l podsvc -c \
  "XDG_RUNTIME_DIR=/run/user/2000 systemctl --user daemon-reload"'

Start, stop, or restart only docs-static.service during the approved action.

Upgrade, backup, and restore

Use a tested image digest/tag before upgrading. Back up the Quadlet and runner configuration; rebuild the artifact from Git. An artifact snapshot may be kept for rapid recovery. Restore into a new directory, validate index.html, ownership, modes, and HTTP, then swap atomically. Keep the old directory for rollback.

Monitoring and security

  • Monitor unit state, backend 200, public 200, and artifact age.
  • Treat a failed Forgejo post-publish backend or ingress check as a failed deployment even when the artifact copy completed.
  • Keep the artifact mount read-only.
  • Do not expose port 30084 beyond intended DMZ/ingress sources.
  • Do not add tokens or ACME material; TLS belongs to CT 103.

Troubleshooting

Symptom Cause Diagnosis Remedy
403 Directory/file traversal denied namei -l and stat publish path Restore least-privilege read/execute modes
404 Missing index.html or wrong mount Inspect Quadlet and file presence Rebuild/publish correct artifact
Connection refused Unit/container down User systemd and Podman status Repair/restart only this unit
Public 502 with backend 200 CT 103 route/network Use Caddy runbook Repair ingress layer
Old content Artifact not updated Compare Git/job/file/HTTP times Use publishing runbook