# narty27 Static hosting for a single self-contained `viz.html` (narty 2027 / Saalbach KB export) on PIHA. Plain `nginx:alpine` serving one Docker named volume — no build, no database, no dependencies. - URL: `http://192.168.31.5:8240/viz.html` (and `/` — same file, see below) - Exposure: private (LAN/Tailscale only; no npm vhost, no public ingress) - Volume: `narty27_narty27_content` → `/usr/share/nginx/html:ro` ## Content is personal and lives outside the repo The visualisation is personal content. It is **never committed** — not to this repo, not to any other. It exists in exactly two places: 1. the source on SOLARIA (`~/narty-2027/saalbach-kb/viz.html`), and 2. the `narty27_narty27_content` Docker volume on PIHA. There is no backup job and no bind mount under `/opt/homelab/data/`. If the volume is lost, re-run the update procedure below from SOLARIA. Two copies of the same file are stored in the volume: `viz.html` (canonical name) and `index.html` (so the bare root `http://192.168.31.5:8240/` works without a path). Both must be refreshed together on every update. ## Updating the content (from SOLARIA) ```bash # 1. Ship the file to PIHA scp ~/narty-2027/saalbach-kb/viz.html piha:/tmp/viz.html # 2. Write BOTH copies into the volume via a throwaway helper container ssh piha 'docker run --rm \ -v narty27_narty27_content:/content \ -v /tmp:/src:ro \ alpine sh -c "cp /src/viz.html /content/viz.html && cp /src/viz.html /content/index.html"' # 3. Drop the staging copy ssh piha 'rm -f /tmp/viz.html' # 4. Verify (no container restart needed — nginx serves from disk per request) curl -sf -o /dev/null -w '%{http_code}\n' http://192.168.31.5:8240/viz.html curl -sf -o /dev/null -w '%{http_code}\n' http://192.168.31.5:8240/ ``` **Why a helper container and not `docker cp`:** the volume is mounted `:ro` into the nginx container, and `docker cp` writes through the container's mount namespace — it fails with `mounted volume is marked read-only`, for both running and stopped containers (verified 2026-07-31). The helper container mounts the same volume read-write and bypasses nginx entirely. ## First start `nginx:alpine` seeds an empty named volume with its own default `index.html` and `50x.html` on first run, so the root will show the nginx welcome page until step 2 above overwrites `index.html`. Until then `/viz.html` 404s and the container healthcheck reports unhealthy — this is expected, not a failure. ## Operations ```bash docker compose -f services/narty27/docker-compose.yml up -d # on PIHA ./healthcheck.sh # container + both paths docker volume ls | grep narty27 # narty27_narty27_content ```