Deploying Documentation
Merging a pull request into main deploys production. There is no build step to
run, no artifact to upload, and no image to push. Cloudflare Pages watches this
repository and rebuilds int-docs.thephenom.app on every push to main.
The one rule
int-docs.thephenom.app is deployed by merging into main. That is the entire
promotion path.
If you have merged your PR, you have deployed. If you have not merged, nothing you do locally, in Docker, or on a testing host will reach production.
How production is built
Cloudflare Pages is connected directly to Phenom-earth/phenom-earth-docs through
its git integration. No GitHub Actions workflow is involved.
| Setting | Value |
|---|---|
| Pages project | int-docs-phenom |
| Cloudflare account | 0b61574326148cb1e1a82f00cd160b30 |
| Production branch | main |
| Hugo environment | int-docs (see config/int-docs/) |
HUGO_VERSION |
0.163.0 |
NODE_VERSION |
24 |
The int-docs environment is deliberately richer than the Hugo default: it includes
the internal-only/ content that must appear on the internal docs site. Build
settings live in the Cloudflare Pages dashboard, not in this repository, so
netlify.toml in the repo root is a leftover from the Docsy upstream template and
does not drive the production build.
Sequence
- Open a PR against
main. - Cloudflare Pages builds a preview deployment for the PR and reports it as the
Cloudflare Pagescheck. A green check means your branch compiles; it does not mean anything has shipped. - Merge the PR.
- Cloudflare Pages builds
mainand deploys it toint-docs.thephenom.app, typically within a few minutes. - Verify by loading the affected page and confirming your content is actually served. Do not treat the deployment status alone as proof.
What is not a deploy path
The Docker image is a personal testing instance, not a promotion path.
int-docs.matthewstevens.org is M’s own Hugo container built from
Dockerfile.int-docs. It exists so M can preview the site; it is not shared team
infrastructure and it never promotes anything to production. Content that lives only
in a working tree can appear on that host while being entirely absent from git, which
is exactly how unreviewed content reaches a running site unnoticed.
Other developers must run their own testing container. Do not expect access to
M’s instance. Build your own from Dockerfile.int-docs and run it locally.
The GitHub Actions deploy workflow is gone, on purpose.
deploy-cloudflare-pages.yml was removed in #360
because it pushed a different artifact (the default Hugo environment, without
internal-only/) to the same Pages project. With both pipelines live, the two would
alternately overwrite each other on every push to main. Do not reintroduce it.
mermaid-validate.yml and deploy-mcp-worker.yml are unrelated and stay.
Running the docs locally
Either approach works for previewing your own changes before you open a PR.
# Hugo directly (fast iteration, live reload)
npm install
npm run serve
# Or your own testing container, mirroring the deployed layout
docker build -f Dockerfile.int-docs -t my-phenom-docs .
docker run --rm -p 9666:9666 my-phenom-docs
Gotchas
Hugo and Node versions are coupled, and the coupling has broken production before.
Hugo 0.155+ passes Node --permission flags to node-based asset tools, and Hugo 0.162+
calls node:module registerHooks(), which only exists in Node 22.15+ and 24+. Node 20
is a dead zone: too new for Hugo’s legacy path, too old for the modern one. This
combination failed a production deploy and was fixed in
#329 by moving
Cloudflare Pages to Node 24 and disabling security.node.permissions in
config/_default/config.yaml.
Five places pin a Hugo version, and they can drift apart. All are currently on 0.163.0; keep them that way.
| Location | Purpose |
|---|---|
HUGO_VERSION in the Cloudflare Pages dashboard |
production |
HUGO_VERSION in Dockerfile.int-docs (builder and runtime stages) |
personal testing container |
HUGO_VERSION in Dockerfile |
the other container build |
hugo-extended in package.json |
local npm run builds |
The version named in CLAUDE.md |
what agents read as the project’s Hugo |
Bump them together and confirm the resulting Hugo runs on Node 24 before merging. A bump that moves only one of the five leaves the lanes building on different Hugo versions, so a green local build stops predicting production.
A green Cloudflare check does not validate a Hugo bump. Cloudflare installs
Hugo from its own HUGO_VERSION dashboard variable, so a pull request that
raises hugo-extended in package.json gets a passing preview without ever
running the bumped binary. Hugo 0.164.0 reached a green check that way while
being unbuildable here (template "td-content-after-header" not found). Build
the bumped version locally before believing the check.
PostCSS only runs in production builds. Docsy gates the PostCSS and
autoprefixer pipeline behind hugo.IsProduction, so verifying a PostCSS or
autoprefixer bump requires --environment production. An --environment int-docs build never executes that code path.
Access control
The deployed site is gated by Cloudflare Access.
- An Access policy is configured for
int-docs.thephenom.app. - It authenticates against the phenom-dev-local Cognito user pool
(
us-east-1_AkG9mnbjA). - Only users with valid credentials in that pool can view the site.
Because access is gated, treat published content as internal rather than secret. The gate controls who can read the site; it is not a substitute for keeping credentials and unreviewed material out of the repository.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.