feat: capture staging-verification lessons from iWP Cache deployment
New skill wordpress-plugin-staging-verification: use the persistent staging-1 jail (real-world plugin set left active) instead of a disposable clean-room jail, full verification checklist, and the cosmetic-proc_open-error-vs-real-fatal distinction. Extended bastille-jail-provisioning: bastille0-loopback-missing gotcha (pass the interface explicitly), IP-alias-can-silently-fail-after- recreate gotcha, host-reverse-proxy-vs-jail-IP curl mixup, Valkey as part of the stock stack (correct FreeBSD package name, ACL auth requirement). Extended wordpress-plugin-conventions: drop-in source files (object-cache.php/advanced-cache.php logic) must be excluded from any glob-based plugin autoloader, or they redeclare WP core's own wp_cache_*() functions and fatal -- real bug found and fixed live during iWP Cache's first staging activation attempt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,3 +80,69 @@ Write the generated DB password (and any other generated secrets) to a
|
||||
durable location the moment they're generated — if a later provisioning
|
||||
step fails, an unrecorded generated password is otherwise lost with no
|
||||
way to recover it short of resetting it.
|
||||
|
||||
## `bastille create` interface gotcha: don't assume `bastille0` exists
|
||||
|
||||
Some hosts' `bastille.conf` still has the default
|
||||
`bastille_network_loopback="bastille0"` / `bastille_network_vnet_type="if_bridge"`
|
||||
values even though the host actually uses plain shared-IP aliasing on the
|
||||
physical interface (`vtnet0`) for every real jail on it — confirmed on the
|
||||
`staging` host (100.104.61.54) 2026-08-02. A bare `bastille create <name>
|
||||
<release> <ip>` fails with `[ERROR]: bastille0 interface does not exist`.
|
||||
**Check an existing working jail's `jail.conf`** (`ip4.addr = vtnet0|<ip>;`)
|
||||
to see which interface this host actually uses, then pass it explicitly:
|
||||
```bash
|
||||
bastille create <name> <release> <ip> vtnet0
|
||||
```
|
||||
|
||||
## After create/destroy churn, verify the IP alias actually attached
|
||||
|
||||
A `bastille create` immediately following a `bastille destroy` of a jail
|
||||
that held the *same* IP can silently fail to (re-)add the alias — the jail
|
||||
comes up "running" but has zero network connectivity (DNS and raw-IP fetch
|
||||
both time out, no error surfaced anywhere). Confirmed live 2026-08-02: `pkg
|
||||
install` inside the new jail failed with "Non-recoverable resolver failure"
|
||||
on the first attempt, which looks exactly like a DNS/resolv.conf problem
|
||||
but wasn't (resolv.conf was correct and identical to a working jail).
|
||||
**Verify directly** before assuming jail networking is up:
|
||||
```bash
|
||||
ifconfig <interface> | grep <jail-ip> # must show the alias
|
||||
bastille cmd <jail> fetch -o /dev/null http://1.1.1.1 # raw IP, bypasses DNS entirely
|
||||
```
|
||||
If the alias is missing, `bastille restart <jail>` (not just `start`) or a
|
||||
manual `ifconfig <interface> inet <ip> netmask 255.255.255.255 alias`
|
||||
reapplies it.
|
||||
|
||||
## Testing a jail before its public vhost exists: hit the jail IP, not the host
|
||||
|
||||
If the site's domain isn't wired into the host's reverse-proxy nginx yet
|
||||
(no `sites-enabled/<jail>.conf`), curling the **host's** own `127.0.0.1`
|
||||
with a `Host:` header does NOT reach the new jail — it falls through to
|
||||
whichever `server_name` on the host's nginx matches first (often a
|
||||
different site's `default_server`), returning a normal-looking `200 OK`
|
||||
for entirely the wrong site's content. This produced a long, wrong-track
|
||||
debugging session on 2026-08-02 (chasing a "cache never writes" theory
|
||||
against a site that was never actually being hit). **Always curl the
|
||||
jail's own IP directly** until the real vhost is wired:
|
||||
```bash
|
||||
curl -H 'Host: <domain>' http://<jail-ip>/
|
||||
```
|
||||
Only switch to testing via the host's `127.0.0.1` (or the public domain)
|
||||
once the `sites-enabled` vhost for this specific site actually exists.
|
||||
|
||||
## Stock stack includes Valkey, not just nginx/php/MariaDB
|
||||
|
||||
The fleet's standard per-host stack (see `docs/server-funky.md`) is
|
||||
nginx + php-fpm + MariaDB (host-local) + **Valkey** (host-local, ACL-auth'd,
|
||||
`aclfile`-based, admin password at `/root/.valkey_admin_pw`). A host that's
|
||||
never hosted a Redis/object-cache-dependent site before may be missing
|
||||
Valkey entirely — install and configure it the same way (`bind 127.0.0.1
|
||||
<host-lan-ip>`, `aclfile`, admin password file) rather than treating it as
|
||||
optional, so any plugin/site expecting a persistent object cache gets a
|
||||
real one to test against, not silently falls back to a weaker default.
|
||||
Inside a jail that needs to reach it, install the PHP Redis extension —
|
||||
**package name is `php85-pecl-redis`** (not `php85-redis`, which doesn't
|
||||
exist under that name in the FreeBSD ports tree) — and remember Valkey's
|
||||
ACL means any code connecting to it needs an explicit `AUTH` step, not just
|
||||
`connect()`; a `connect()`-only implementation will silently fail every
|
||||
subsequent command with `NOAUTH` against this fleet's standard Valkey setup.
|
||||
|
||||
Reference in New Issue
Block a user