Pod symptoms
ErrImageNeverPull for the site pod
You set image.pullPolicy=Never (typical for kind dev) but the image
isn’t loaded into the cluster.
ImagePullBackOff on bitnami/* images
Bitnami withdrew their public Docker Hub images in 2025. The chart’s
default values point at bitnamilegacy/* — verify yours match. If you
override mariadb.image.repository etc., make sure the override still
exists.
CreateContainerConfigError: secret not found
The site pod references a Secret that doesn’t exist yet. Common causes:
- You set
keysSalts.existingSecretbut the named Secret hasn’t been created. Either create it (External Secrets Operator, kubectl, etc.) or setkeysSalts.autoGenerate: true. - You disabled a subchart (
mariadb.enabled: false) but didn’t supplyexternalDatabase.existingSecretfor the password. - Subchart resource naming mismatch — bitnami subcharts produce
<release>-<subchart>for their resources, not<release>-<our-chart>-<subchart>. Our_helpers.tplaccounts for this; if you’ve overriddennameOverrideorfullnameOverrideit might drift.
exec: frankenphp: Operation not permitted
You’re running with containerSecurityContext.allowPrivilegeEscalation: false against an runtime image older than the setcap -r patch. Older images carry cap_net_bind_service=ep on the binary; the kernel refuses to exec under no_new_privs.
Fix: upgrade to a newer runtime (the cap is stripped at build time on current images), or temporarily set allowPrivilegeEscalation: true in your values.
Cache symptoms
Souin returns stale content after save_post
The SouinInvalidator is supposed to DEL the cache key on save. If
content is stale beyond the TTL window:
- Verify Redis connectivity — exec into the site pod and try to ping Redis manually:
- Check the WP error log —
error_loglines from[FrankenPress\\SouinInvalidator]indicate connection or DEL failures. - Verify the cache key shape — Souin uses
GET-<scheme>-<host>-<path>. If the WP scheme/host returned byhome_url()differs from what Souin sees on the request, the keys diverge. SetWP_HOMEandWP_SITEURLto exactly the externally-visible URL (including scheme), and ensure the Ingress / HTTPRoute passesX-Forwarded-Protocorrectly.
Cache-Status: Souin; fwd=bypass; detail=UPSTREAM-ERROR-OR-EMPTY-RESPONSE
WordPress returned an empty response. Common causes:
- No theme installed —
roots/wordpressis no-content (no bundled themes). Make sure the site composer-installs at least one theme (site-templateshipswpackagist-theme/twentytwentyfiveby default). - PHP fatal during request — check the site pod logs for stack traces.
- WP not yet installed — the install Job hasn’t completed yet (or is disabled). Check
kubectl --namespace <ns> get jobsfor the<release>-site-installJob; tail the most recent pod’s logs for the failure. IfsiteInstall.enabled=false, runwp core installmanually viakubectl exec.
S3 symptoms
”FrankenPress: media uploads disabled” error in WP admin
S3UploadsBootstrap registered wp_handle_upload_prefilter to refuse
uploads. The chart’s ConfigMap is missing one of the required env vars
(FP_S3_BUCKET, FP_S3_KEY, or FP_S3_SECRET).
Check:
externalS3.*.
InvalidAccessKeyId from S3 SDK
The AWS SDK is hitting s3.amazonaws.com with credentials that don’t
exist there. Two common causes:
FP_S3_ENDPOINTnot honoured — mu-plugin v0.1.0 had a bug where humanmade/s3-uploads’S3_UPLOADS_ENDPOINTconstant was defined but not applied to the SDK. Upgrade to v0.1.1 or later (the bake default inruntime).- Real AWS credentials misconfigured — verify
FP_S3_KEYandFP_S3_SECRETare correct, and the IAM principal hass3:GetObject/s3:PutObject/s3:DeleteObject/s3:ListBucketon the bucket.
0-byte uploads to S3
Media uploads land in S3 with the right key + path, butContent-Length: 0 and ETag d41d8cd98f00b204e9800998ecf8427e (the MD5 of an empty
file). WordPress shows the attachment as broken; thumbnails are also
empty.
Cause: humanmade/s3-uploads sends x-amz-acl: public-read on every PUT
by default. Buckets with Object Ownership = “Bucket owner enforced”
(the AWS new-bucket default since April 2023, ACLs disabled) reject the
ACL header and abort the PUT mid-stream, leaving the 0-byte object
behind. Same root cause as
humanmade/S3-Uploads#587
and #158.
Fix: on mu-plugin v0.2.0+ this is the default — FP_S3_OBJECT_ACL
is unset, the bootstrap defines S3_UPLOADS_OBJECT_ACL as null, and
no ACL header is sent. Bump:
FP_S3_OBJECT_ACL set:
config/application.php:
Logged-out browser sees logged-in admin content
Admin pages were being served from cache. WP signalsCache-Control: no-store, private on /wp-admin/ and the REST API, but cache-handler
v0.16.0 doesn’t reliably honour those directives.
Fixed in runtime v0.1.4+: the global cache block uses
regex.exclude to skip every auth-aware path (/wp-admin/,
/wp-login.php, /wp-cron.php, /xmlrpc.php, /wp-json/, including
Bedrock /wp/... variants), and a server-block matcher routes any
request carrying a wordpress_logged_in_*, wp-postpass_*, or
comment_author_* cookie around the cache directive entirely. See
Cacheability model.
If you have custom auth-aware paths (membership area, signed-media
endpoints), set FP_CACHE_BYPASS_EXTRA — appended verbatim to the
bypass regex, so use an alternation fragment with a leading |:
Edits saved but not reflected on view
Post edit / trash / permanent-delete in wp-admin appears to succeed (“Saved” / “Moved to trash”) but the public URL keeps serving the old content until the cache TTL expires. Two distinct bugs converged in older releases:- Scheme mismatch — Souin keyed cache entries with
httpwhile WordPress emittedhttps://...URLs for invalidation. Fixed in runtime v0.1.3+ (Caddy now trusts the upstream proxy’sX-Forwarded-Proto) and mu-plugin v0.2.1+ (DELs both schemes defensively). - Hook timing —
deleted_postfires AFTER the row is gone, soget_permalink( $post_id )returns false and the invalidator no-ops. Fixed in mu-plugin v0.2.2+ (also hookswp_trash_postandbefore_delete_post, which fire BEFORE the state change with the permalink still resolvable).
Helm symptoms
INSTALLATION FAILED: ... namespace ... is being terminated
You uninstalled but the namespace is still deleting. Wait, then retry:
Error: looks like "..." is not a valid chart repository
You’re using OCI registry syntax (oci://...) on a Helm version older
than 3.8. Upgrade Helm: brew upgrade helm (or your platform
equivalent).
helm dependency update: image pull failure
Bitnami’s OCI registry occasionally rate-limits anonymous pulls. Run
helm dependency update again, or authenticate to the registry first
with helm registry login registry-1.docker.io.
WordPress symptoms
Bedrock-autoloader DB errors during wp core install
You’ll see error lines like:
wp core install because
roots/bedrock-autoloader tries to write its discovery cache to
wp_options before WP creates the table. The install completes
successfully despite the noise; subsequent boots are clean. Not
blocking; ignore.
”Update WordPress” button is grayed-out / missing
This is by design. The lockdown constants (DISALLOW_FILE_MODS,
AUTOMATIC_UPDATER_DISABLED) are hard-coded in config/application.php
and config/environments/production.php. Updates go through image
rebuilds — see Upgrading.
Browser shows “unknown app” / 301 redirect loop on localhost:8080
You’re port-forwarding to the site Service but the browser ends up at
http://site.localhost/ (or http://localhost/) and OrbStack /
your DNS resolver shows “unknown app” or a connection-refused page.
The chart defaults WP_HOME to http://site.localhost, and
WordPress 301-redirects any request whose Host header doesn’t match.
When you kubectl port-forward to localhost:8080, the browser sends
Host: localhost:8080, which WP rejects.
Fix it by setting site.url to the URL you’ll actually browse to:
port-forward svc/<release>-site 8080:80
and browse to http://localhost:8080/.
For a real deploy, set site.url to your public URL and enable
ingress.* or httpRoute.* instead of port-forwarding.