ops: phasefinal.com moved to Cloudflare Pages; pfi-ana-webhost destroyed and removed from inventory, DNS and the FortiGate

This commit is contained in:
vh
2026-10-04 17:28:29 -07:00
parent 55e4d01b49
commit 513f6ef38f
19 changed files with 138 additions and 102 deletions
+25 -25
View File
@@ -1,7 +1,11 @@
# phasefinal-web
The Phase Final, Inc. corporate site — nine static pages served by nginx on
**ana-docker**, fronted by Traefik at `www.phasefinal.com`.
The Phase Final, Inc. corporate site — nine static pages on **Cloudflare Pages**
(project `phasefinal`, account `vh@phasefinal`, `phasefinal.pages.dev`), at
`www.phasefinal.com` and `phasefinal.com`. Moved off ana-docker 2026-10-04 (Prime);
the nginx container there was retired (`/opt/docker/{compose,conf}/phasefinal-web.retired-20261004`).
`conf/site/` is the deployed tree, including `_headers` (the response headers nginx used
to set) and `404.html` (without it Pages serves the home page for every unknown path).
Built from the design brief at `docs/design/` (boothed 2026-08-29); the markup
and CSS came from a Claude Design session and are checked in verbatim under
@@ -21,35 +25,31 @@ requests rule. Both must be undone on import:
Without these the page silently falls back to system fonts and looks wrong
rather than broken, which is the failure mode you won't notice.
## Routing — and the healthcheck trap that looks like a routing bug
Routers come from the compose labels: `phasefinal-web` serves
`www.phasefinal.com`; `phasefinal-apex` 301s `phasefinal.com` to it.
⚠ **Traefik silently skips containers Docker reports as unhealthy.** There is no
error, no log line, and no router — it looks exactly like a broken provider. The
first deploy of this stack hit that: the healthcheck used
`http://localhost/`, which resolves to `::1` in `nginx:alpine`, and nginx listens
on IPv4 only, so the check got "Connection refused" forever and the container
never left `unhealthy`. **Use `127.0.0.1`, never `localhost`, in a healthcheck
for an IPv4-only listener** — and if a correctly-labelled container never appears
in `/api/http/routers`, check `docker inspect --format '{{.State.Health.Status}}'`
*before* suspecting Traefik.
## Deploy
```bash
scripts/deploy-stack.sh ana-docker phasefinal-web
ssh infra-ops@10.250.50.70 'cd /opt/docker/compose/phasefinal-web && docker compose up -d'
stacks/phasefinal-web/deploy-pages.sh # conf/site → Pages production (branch main)
```
## DNS
Needs a vaulted Cloudflare token with Account → Cloudflare Pages: Edit (see the script).
⚠ The 2026-10-04 migration used jackdaw's token as a one-off; a dedicated
`nh3-dev/.config/cloudflare/infra-ops-pages-token` is pending Prime. Check the result on
the per-deploy preview URL wrangler prints before trusting the custom domains.
`www.phasefinal.com` → A `38.120.12.44` (the Anaheim public IP, forwarded to
ana-docker's Traefik). The apex `phasefinal.com` has **no** record — the site is
www-only by operator decision. Cloudflare-proxied is the intended end state, for
edge caching and the automatic email-address obfuscation the contact block
relies on.
## DNS and redirects
`www.phasefinal.com` and `phasefinal.com` → CNAME `phasefinal.pages.dev`, Cloudflare-proxied
(both are Pages custom domains). Cloudflare-proxied is also what provides the email-address
obfuscation the contact block relies on. ⚠ **The apex no longer 301s to www**: Traefik used
to do that, Pages `_redirects` cannot redirect between hostnames, and no vaulted token may
create a zone Redirect Rule. Both hostnames serve the site until Prime mints a token with
Single Redirect: Edit; then add the rule `phasefinal.com/* → https://www.phasefinal.com/${1}` (301).
## Lesson from the ana-docker era (2026-08-29)
Traefik silently skips containers Docker reports as unhealthy: no error, no router. The first
deploy's healthcheck used `http://localhost/`, which is `::1` in `nginx:alpine` while nginx
listened on IPv4 only. Use `127.0.0.1` in healthchecks for IPv4-only listeners.
## Content constraints