ADR-016: Documentation Hosting via MkDocs, GitHub Pages, and In-Cluster Nginx¶
Status¶
Accepted
Date¶
2026-03-21
Context¶
The repository contains a growing set of Markdown documentation under docs/ covering ADRs, operational runbooks, architecture overviews, and contributing guides. This content is only readable by navigating the raw GitHub file tree. There is no rendered, searchable, or hyperlinked view of the docs accessible to users of the project.
Two access patterns are desirable:
- Public/remote — a rendered site accessible without cloning the repo, linked from the README and GitHub repository page.
- In-cluster/local — accessible from within the local Minikube environment alongside other cluster services (e.g.
grafana.local,keycloak.local), consistent with the cluster-first philosophy of the project.
The repo already has mkdocs.yml configured with the Material theme and docs/requirements.txt pinning the pip dependencies. The authoring toolchain is therefore already decided; only the hosting mechanism is open.
Decision¶
Authoring: MkDocs + Material theme (existing)¶
No change. mkdocs.yml remains the single source of truth for site structure. docs/requirements.txt is the canonical pip dependency file for all doc builds.
Public hosting: GitHub Pages via GitHub Actions¶
A workflow at .github/workflows/docs.yml triggers on pushes to main that touch docs/**, mkdocs.yml, docs/requirements.txt, or the docs Dockerfile/nginx config. It:
- Installs pip dependencies from
docs/requirements.txt. - Runs
mkdocs build --strictto catch broken links. - Uploads the built
site/directory as a GitHub Pages artifact. - Deploys it via
actions/deploy-pages.
In parallel, the same workflow builds a Docker image (multi-stage: Python/MkDocs builder → nginx:alpine) and pushes it to ghcr.io/marcspeckmann/homekube-docs tagged with both latest and the short Git SHA.
In-cluster hosting: nginx Deployment at https://docs.local¶
A Kubernetes Deployment in the docs namespace runs the ghcr.io/marcspeckmann/homekube-docs image. The image is built from apps/docs/Dockerfile and serves the pre-built static site via nginx.
Resources added:
apps/docs/
Dockerfile # multi-stage: MkDocs build → nginx:alpine
nginx.conf # serve MkDocs directory URLs; /healthz health endpoint
namespace.yaml # docs namespace
deployment.yaml # Deployment + Service + Ingress (docs.local + TLS)
network-policies.yaml # default-deny + allow ingress-nginx + allow DNS egress
kustomization.yaml # Kustomize manifest list
clusters/local/apps.yaml # Flux Kustomization object (depends on infrastructure)
Ingress: https://docs.local with cert-manager TLS (local-ca-issuer), consistent with existing services.
Image management: The Deployment manifest carries a Flux image-policy annotation (# {"$imagepolicy": "flux-system:homekube-docs"}), ready for image automation (ADR-015) once a corresponding ImageRepository and ImagePolicy are configured.
Alternatives Considered¶
| Option | Reason Not Chosen |
|---|---|
| GitHub Pages only (no in-cluster) | Breaks parity with other cluster services; no local-only access |
| Read the Docs | External service dependency; overkill for a personal sandbox |
| Serve docs directly from Git clone (init container) | Requires git and pip in the running container; larger image and slower startup |
| ConfigMap-mounted HTML | Impractical for a full MkDocs site (~100+ files); ConfigMap size limits apply |
Separate docs repository |
Fragments the monorepo; ADR-003 explicitly prefers a single repo |
Consequences¶
Positive¶
- Documentation is rendered and searchable at both a public URL and
https://docs.local mkdocs build --strictin CI catches broken internal links before merge- Image is tiny (
nginx:alpine+ static HTML — typically < 50 MB) - Consistent with existing ingress + TLS pattern used by Grafana and Keycloak
- GitHub Pages URL is automatically linked from the GitHub repository page
Negative¶
- CI must rebuild and push the Docker image on every qualifying push; adds ~2 min to the pipeline
ghcr.ioimage requires a GitHub token for the cluster to pull (or the image must be public)- Image automation setup (ADR-015
ImageRepository/ImagePolicy) is a follow-up step before the in-cluster deployment auto-updates on new pushes
Setup Checklist¶
- Enable GitHub Pages in repository Settings → Pages → Source: GitHub Actions.
- Ensure the
homekube-docsimage onghcr.iois set to public (or create an imagePullSecret in thedocsnamespace). - Add
docs.localto/etc/hosts:echo "$(minikube ip) docs.local" | sudo tee -a /etc/hosts. - After Flux reconciles
apps, verify:kubectl get pods,ingress -n docs. - Open
https://docs.local(accept self-signed cert from local CA).