PSW Documentation Site#
What Is It?#
PSW Documentation is the same set of concept docs you’re reading right now — every file under docs/concepts/ plus the project README
— rendered as a real, searchable, browsable website and shipped with your self-hosted solution. It lives at https://docs.<your-domain>/ (e.g. https://docs.casaeureka.ca/) and is served by your own server, alongside Jellyfin
, Grafana
, and everything else.
So the next time you wonder “wait, what does idempotency mean again?” or “how do I expose an app remotely?”, you don’t need to fish for a GitHub tab — open docs.<your-domain>/ and search.
Why Does It Exist?#
The docs already existed as markdown in the repo. Reading markdown on GitHub is fine for a developer, but a self-hosted setup outlives the day you cloned the repo:
- You might be offline. A power cut, an ISP blip, a flight — your platform keeps running, your docs should too.
- You might be on a different machine that doesn’t have the repo checked out — your phone, your TV-room laptop, a friend’s tablet.
- You shouldn’t have to leave the house’s network just to look up “how does the convergence timer work again?”.
- The site has full-text search across every page; the GitHub markdown viewer does not.
PSW takes the position that documentation is part of the platform, not a side artefact. So the docs ship like any other app — built into a container, deployed on your server, behind the same HTTPS, listed on your homepage dashboard .
Where It Lives#
It’s the eighth core app — installed automatically during bootstrap on the bootstrap target , no extra step required:
| Question | Answer |
|---|---|
| URL | https://docs.<your-domain>/ (e.g. https://docs.casaeureka.ca/) |
| Login required? | No. Docs are public — anyone who can reach the URL can read them. Same sso_type: none reasoning as Authelia
: the docs page can’t gate itself behind a login when most readers are looking up how to log in |
| App folder | psw-apps/psw_apps/psw_docs/
— the meta + the deploy hook |
| Image build | psw-docs/Containerfile
+ psw-docs/nginx.conf
— the two-stage build (Hugo render → nginx serve) |
You’ll find it on the Apps page in the operations dashboard under the Infrastructure category, with a link to its homepage tile too.
How It’s Built#
The docs site is a static Hugo site using the hugo-book theme. Two big choices keep it honest:
- One source of truth. The Hugo site under
site/doesn’t have its own copy of the docs. Hugo’s content adapters read straight fromdocs/concepts/andREADME.mdvia mounts declared insite/hugo.toml. Edit a markdown file underdocs/concepts/and the next render picks it up — no copy step, no sync script, no two-places-to-update. - Same render at home and in production.
make docsruns the Hugo dev server with live reload onhttp://localhost:1313for editing.make docs-buildproducessite/public/. The container build does the samehugo --gc --minifyinvocation. What you see locally is exactly what ships.
The container itself is a tiny nginx:alpine serving the rendered HTML on port 8112 (a free high port — 80/443 are owned by Traefik ). No database, no environment variables, no storage volumes. The whole site is baked into the image at build time.
How It Gets onto Your Server#
Like the PSW Dashboard , the docs image isn’t pulled from a public registry. It’s built on your workstation and shipped to your server as a tarball. The flow during bootstrap :
- Workstation builds the image.
build_docs_image()runspodman buildagainst the repo withsite/+docs/+README.md+psw-docs/in the build context. Hugo fetches the theme module, renders the static site, nginx packages it. Output:/tmp/psw-docs-image.tar. - Tarball travels by SCP. The tarball is copied to the bootstrap target as part of the Prepare target phase — same channel that ships the dashboard image and the PSW source.
- Deploy hook loads the image. Right before the podman quadlet
starts the container, the
@pre_deploy("psw_docs")hook runspodman loadfrom the tarball soImage=localhost/psw-docs:latestresolves locally instead of triggering a registry pull. The tarball is consumed (deleted) on success. - Quadlet starts the container.
Network=host, port 8112, no special caps. Traefik picks up the convention that turns thesubdomain: docsfield inmeta.ymlinto the routing ruleHost(\docs.`)`.
The build mechanics (build context staging, podman build/save, SCP transfer) live in psw_lib.convergence_agent.local_image
and are shared with PSW Dashboard
. The two image-build modules just declare what goes in the build context.
Updating the Docs#
Two paths, depending on whether you’re editing or just consuming:
- Editing (you’re working on PSW itself). Run
make docsto start the Hugo dev server onhttp://localhost:1313. Edit any file underdocs/concepts/orREADME.md, save, and the browser auto-reloads. When you’re happy, commit + push as usual. - Updating the version on your server. Doc updates ship the same way every other PSW change does:
psw self-updatepulls the latest PSW source, and the next bootstrap (or a manualpsw deploy bootstrap --apps psw_docs) rebuilds the image and redeploys. Updates do NOT flow through your user project’s git push → convergence loop, because the docs are part of PSW itself, not part of your project.
What’s on the Site#
Whatever’s in the source tree at build time:
- Home page — your project’s README , rendered at the root URL.
- Concepts section — every file under
docs/concepts/, in a left-hand sidebar with categories (Getting Started, Apps, Infrastructure…) and an inter-page table of contents. - Search — a search box in the top-right indexes every page; results show the matching section. Powered by the hugo-book theme’s Lunr -based client-side search — no Elasticsearch, no third-party service.
- Light / dark theme — auto-follows your OS preference, with a toggle in the header.
Internal links between concept docs (like the [bootstrap](bootstrap.md) references all over this doc) are rewritten to clean URLs by Hugo, so they keep working in both the rendered site and on GitHub.
What It’s NOT#
- Not editable from the site. The docs are a static render. You can’t write notes inside it, leave comments, or reorganise the sidebar from the browser. To change content, edit the markdown in the repo.
- Not the operations dashboard. “Doc” sounds vague — the operations dashboard
at
dashboard.<your-domain>is where you operate the platform. The docs site atdocs.<your-domain>is where you learn how it works. Different URLs, different jobs. - Not user-installable. You can’t
psw app remove psw_docsand expect a meaningful outcome — it’s a core app , part of the platform’s foundation. Same status as PSW Dashboard, Traefik , or LLDAP . - Not your project’s docs. It always renders PSW’s docs, not anything you might write inside your user project
. If you want notes about your setup, that’s a separate problem — most operators keep a
README.mdnext to theirnetwork.ymland call it a day.
Key Ideas#
- Eighth core app — deployed automatically by bootstrap , no extra config to enable it
- Public, no login — anyone who can reach
docs.<your-domain>can read; the docs explain how to log into everything else, so they can’t gate themselves - One source of truth — Hugo content adapters render
docs/concepts/andREADME.mddirectly from the repo; no copies, no sync scripts - Workstation-built, SCP-shipped — same pattern as PSW Dashboard; no public registry round-trip
- Updates with PSW itself —
psw self-update+ a redeploy, not via your project’s git push loop - Local edit loop —
make docsfor live-reload editing; what you see locally is exactly what ships
See Also#
- Apps — the catalog and the difference between core and user apps
- Bootstrap — the one-time process that deploys this doc site (and the seven other core apps )
- Operations Mode — the other PSW-built web UI on your server, the one that lets you act on what these docs explain
- Conventions
— how
subdomain: docsinmeta.ymlbecomes a working HTTPS URL with no per-app code