WHA Docs
Infrastructure

Operations

Health monitoring, ephemeral storage, logging and practical guidance for running the site day to day.

Health monitoring

The /healthz endpoint is a JSON health check provided by the yax framework (yax-common). It answers in layers:

  • /healthz — liveness. Returns 200 once the database, Redis and PHP are responding. Fast and dependency-light; this is what an uptime monitor should hit. A 200 means the origin is actually handling requests.
  • /healthz?detail — adds a per-subsystem health summary for everything WHA actually runs: scheduled cron, Meilisearch, media offload and image conversion, error reporting, and the Doctor.com and Press Ganey syncs. ?all adds verbose detail; ?cache=false forces a live read (deeper views are cached briefly).

A failing subsystem is reported without failing the overall check — a stale Doctor.com sync, a dead cron, or an unreachable search index shows up as a clear ok: false in the detail view while liveness stays 200. So uptime stays meaningful ("is the site up?") and the detail view is the developer's at-a-glance verification lens for the integrations ("is everything actually working?"). The framework contract for how each subsystem reports lives in the per-plugin documentation.

Uptime monitoring setup

Point your monitoring service (Uptime Robot, Uptime Kuma, etc.) at:

https://whallc.com/healthz

The endpoint bypasses both Cloudflare WAF rules and cache rules, so a 200 response means the origin server is actually handling requests, not just Cloudflare serving a cached page.

Do's and don'ts

DoDon't
Monitor /healthz — it hits the origin directlyMonitor the homepage — Cloudflare may serve a cached 200 even if the origin is down
Set check intervals to 1–5 minutesCheck more frequently than every minute — it's unnecessary and adds load
Alert on consecutive failures (2–3)Alert on a single failure — transient network blips cause false alarms
Monitor both production and stagingAssume staging is fine because production is up — they're separate deployments

Ephemeral storage

Heroku dynos have an ephemeral filesystem. Any files written to disk during runtime are lost when the dyno restarts, which happens:

  • On every deploy
  • At least once every 24 hours (Heroku's daily dyno cycling)
  • When scaling or restarting manually

What this means

  • Media uploads must go to S3. Media offloading (the yax-offload-media plugin) handles this automatically: uploads are written to S3 and URLs are rewritten. Files aren't stored on the dyno filesystem.
  • Don't rely on local file writes. Anything that writes to /tmp or the filesystem (log files, generated reports, cached files) will disappear on restart.
  • WordPress core and plugins are part of the deploy artifact. They're baked into the dist/ build, not installed at runtime. That's why the build step exists: everything the application needs is in the artifact.
  • composer install runs on container startup. The entrypoint script makes sure dependencies are present, but it's a safety net. The deploy artifact should already be complete.

Common pitfalls

  • Poorly coded plugins — Any plugin that uses the WordPress upload API works fine, since media offloading handles S3 automatically. Problems only arise with plugins that bypass the upload API and write directly to the filesystem.
  • WordPress debug logwp-content/debug.log is written to the ephemeral filesystem. It's useful for debugging during a session but don't rely on it persisting. Use heroku logs instead.
  • Scheduled tasks run from an external 5-minute cron rather than page visits, so they fire reliably regardless of traffic. See Scheduled tasks below.

Logging

Heroku logs

Application logs are available via the Heroku CLI:

# Tail all logs
heroku logs --tail --app womens-healthcare-associates

# Filter to web process
heroku logs --tail --app womens-healthcare-associates --ps web

# Recent logs (last 1500 lines)
heroku logs -n 1500 --app womens-healthcare-associates

Local development

# All container logs
npm run logs

# Specific service
npm run logs:wordpress
npm run logs:nginx
npm run logs:db

# Log viewer UI
# Dozzle at localhost:8888

What gets logged

  • Nginx — Access logs and error logs. Shows request URLs, response codes and upstream PHP-FPM errors.
  • PHP-FPM — PHP errors, warnings and notices. Controlled by WORDPRESS_DEBUG and WORDPRESS_DEBUG_LOG env vars.
  • WordPress — When WP_DEBUG_LOG is enabled, WordPress writes to wp-content/debug.log (ephemeral on Heroku).
  • Audit log — The audit log (the yax-audit-log mu-plugin) tracks admin actions (post edits, plugin changes, option updates) in the database, not the filesystem.

Deploys

Pre-deploy checklist

  1. Theme assets built? Run npm run production in the theme directory if CSS/JS changed. Commit assets/dist/.
  2. Tests pass? Run npm test if Playwright tests exist for the feature.
  3. Staging verified? Merge to staging first, verify on staging.whallc.com.
  4. Database changes? If the deploy requires database changes (new options, updated ACF field groups), plan for those separately. The deploy only ships code.

Post-deploy verification

  1. Check /healthz returns 200
  2. Spot-check key pages (homepage, provider listing, a provider detail page)
  3. Check the browser console for JS errors
  4. If Cloudflare cache is stale, purge it

Rollback

Heroku keeps previous releases. To roll back:

# See recent releases
heroku releases --app womens-healthcare-associates

# Roll back to previous release
heroku rollback --app womens-healthcare-associates

Scheduled tasks

WordPress cron (wp-cron.php) drives the recurring jobs: the Doctor.com and Press Ganey syncs, cache and log cleanup, and so on. Instead of WordPress's default trigger — which only fires when someone loads a page, and stalls during quiet periods — an external scheduler runs cron every 5 minutes, so jobs fire on a fixed cadence regardless of traffic. (WORDPRESS_DISABLE_CRON controls WordPress's built-in page-visit trigger.)

  • Reliable cadence: scheduled tasks run on time, including overnight and on weekends.
  • Long-running tasks can still hit Heroku's 30-second request limit, so heavy work is batched (the sync plugins already do this).
  • The wp-crontrol plugin is installed for viewing and managing scheduled events in wp-admin.

Database

Backups

A daily offsite backup protects the database and media. See Backups & Restore for what's covered and how to restore. For a manual export:

# Export via WP-CLI (local dev)
npm run wp -- db export backup.sql

# Import
npm run wp -- db import backup.sql

Search and replace

When migrating data between environments, URLs need to be rewritten. The better-search-replace and search-and-replace plugins handle this in wp-admin. For CLI:

npm run wp -- search-replace 'https://whallc.com' 'https://whallc.joeyyax.dev' --dry-run

Always run with --dry-run first.

Version control

All code changes go through Git. The branch workflow and what's tracked are documented in Deployment. Key points:

  • Never edit files directly on the server. Ephemeral storage means changes are lost on the next deploy.
  • Always commit compiled theme assets (assets/dist/) after running npm run production
  • Always commit composer.lock. It pins exact dependency versions.

Managed services

ServiceStatus
Media optimizationActive — images optimized for web delivery
Uptime monitoringActive — /healthz endpoint monitored, alerts on downtime
Error monitoringActive — application errors captured via GlitchTip
Daily offsite backupsActive — database backups stored offsite
Social feedActive — aggregated social media feed

See Contacts for support and escalation information.

On this page