Readpage Now Updates Itself
How a Gitea Actions workflow dispatch and a homelab monitoring dashboard turned a manual deploy step into a fully automated documentation pipeline.

Contents
Where We Left Off#
In a previous post I wrote about building Readpage.
The short version: documentation lives in Git, Hugo generates a static site, a small Python application handles authentication, and the whole thing deploys as a Docker container via Gitea Actions.
It worked well.
But there was still one step that required attention.
When I pushed new documentation to the Homelab-Doc repository, the Readpage site did not update automatically.
The CI pipeline in the Readpage repo was triggered by pushes to its own main branch.
A push to Homelab-Doc was a separate event.
Something had to connect them.
The Manual Step#
The original workflow looked like this:
- Write documentation in Homelab-Doc.
- Push to Homelab-Doc.
- Manually trigger a Readpage rebuild.
Step three was the problem.
Not because it was difficult.
It took about thirty seconds and a few clicks.
The problem was remembering to do it.
And the problem was that the documentation on the portal was silently stale until someone noticed.
Workflow Dispatch#
Gitea Actions supports an event called workflow_dispatch.
When a workflow declares it, the Gitea API can trigger it remotely with a single HTTP request.
on:
push:
branches:
- main
workflow_dispatch: # ← allows external callers to trigger this workflowThe trigger call looks like this:
curl -X POST \
"https://<gitea-host>/api/v1/repos/<owner>/readpage/actions/workflows/build.yml/dispatches" \
-H "Authorization: token <api-token>" \
-H "Content-Type: application/json" \
-d '{"ref":"main"}'One HTTP POST.
That is all it takes to start a full Readpage build and deploy from anywhere.
The Trigger Workflow#
I added a small workflow to the Homelab-Doc repository.
Every time Homelab-Doc is pushed to main, this workflow fires and calls the Readpage build.
name: Trigger Readpage Rebuild
on:
push:
branches:
- main
jobs:
trigger:
runs-on: ubuntu-latest
steps:
- name: Dispatch readpage build
run: |
RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST \
"https://<gitea-host>/api/v1/repos/<owner>/readpage/actions/workflows/build.yml/dispatches" \
-H "Authorization: token ${{ secrets.DISPATCH_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"ref":"main"}')
STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTP_STATUS://')
echo "HTTP $STATUS"
[ "$STATUS" -ge 200 ] && [ "$STATUS" -lt 300 ]The token is stored as a repository secret named DISPATCH_TOKEN.
One thing worth noting: Gitea reserves the GITEA_ prefix for its own built-in variables.
A secret named GITEA_TOKEN will be rejected by the UI.
Use any other name.
The Submodule Reset#
Readpage keeps Homelab-Doc as a git submodule.
readpage/
└── git-files/ ← Homelab-Doc lives hereThe build workflow checks out Readpage, then fetches the latest Homelab-Doc and hard-resets the submodule to the current state of origin/main.
cd git-files
git fetch origin main
git checkout main
git reset --hard origin/mainThis means the submodule pointer committed in the Readpage repo does not need to be bumped every time Homelab-Doc changes.
The trigger fires.
The submodule is reset to current.
The image is built from the latest documentation.
No manual pointer bump required.
The Full Pipeline#
The complete flow from a documentation edit to a live update:
Edit documentation
│
▼
git push Homelab-Doc main
│
▼
trigger-readpage.yml fires (~1 second)
│ POST /api/v1/.../build.yml/dispatches
▼
build.yml starts
│
├── checkout Readpage
├── reset git-files to Homelab-Doc origin/main
├── build search index and recent pages
├── build Docker image
├── push to registry
└── deploy via SSH
│
▼
Live documentation updated (~18 seconds total)From push to live in roughly twenty seconds.
NOAH and Stack Reports#
The pipeline also connects to something larger.
NOAH is the homelab monitoring dashboard I use to track SSH hosts, containers, services, and Proxmox infrastructure.
I added a script to NOAH that queries the database and the API, then writes a raw snapshot into a file called state.md.
From that snapshot, a dated stack report gets generated:
bash NOAH/docs/reports/generate-state.shThe output is a Stack-report-YYYY-MM-DD.md file that lands in the reports/ folder of Homelab-Doc.
NOAH monitors infrastructure
│
▼
generate-state.sh queries DB + API
│
▼
state.md (raw snapshot)
│
▼
Stack-report-2026-06-09.md → Homelab-Doc/reports/
│
▼
git push Homelab-Doc
│
▼
Readpage rebuilds automaticallyThe documentation now reflects the current state of the infrastructure.
Not because someone remembered to update it manually.
Because the tools that monitor the infrastructure also write to the documentation repository.
What Changed#
Before:
- Push documentation.
- Remember to trigger Readpage rebuild.
- Wait.
After:
- Push documentation.
- Everything else happens automatically.
The Readpage portal always reflects the latest state of Homelab-Doc within about twenty seconds of any push.
Stack reports land in the portal automatically when infrastructure state is captured.
Nothing requires manual intervention.
Why This Matters More Than It Sounds#
A documentation portal that requires manual update steps will drift.
Not because anyone intends to let it drift.
Because people are busy.
Because manual steps get skipped under pressure.
Because “I’ll update the docs later” is how documentation becomes outdated.
Automating the connection between the documentation source and the portal removes the human step entirely.
The documentation cannot be stale because there is no manual step to forget.
That is a small but meaningful difference.
Final Thoughts#
The original Readpage setup was already a significant improvement over searching through Git directories with grep.
The auto-update pipeline makes it something better.
Documentation is now a continuous output of the homelab rather than a separate task that competes with everything else.
Write it once.
Push it.
It is live.