From 94c08c20a558f44c39f4174783a3fa84c8622799 Mon Sep 17 00:00:00 2001 From: herbygitea Date: Wed, 15 Apr 2026 11:00:32 +0000 Subject: [PATCH] Create wp-cron-supercronic-deploy.md via n8n --- Tech/Sessions/wp-cron-supercronic-deploy.md | 142 ++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 Tech/Sessions/wp-cron-supercronic-deploy.md diff --git a/Tech/Sessions/wp-cron-supercronic-deploy.md b/Tech/Sessions/wp-cron-supercronic-deploy.md new file mode 100644 index 0000000..28968a8 --- /dev/null +++ b/Tech/Sessions/wp-cron-supercronic-deploy.md @@ -0,0 +1,142 @@ +--- +project: wp-cron-supercronic-deploy +type: session-notes +status: completed +path: Tech/Sessions +tags: + - pbs + - wordpress + - traefik + - docker + - cron +created: 2026-04-15 +updated: 2026-04-15 +--- + +# WP-Cron → Supercronic Sidecar Deployment + +Session notes captured retroactively from March 7 work, before the n8n +email pipeline was in place. + +## Problem + +Uptime Kuma status checks were triggering WordPress's built-in WP-Cron on +every HTTP request, causing cron jobs to fire unpredictably on page load +instead of on a proper schedule. Added unnecessary load and made cron +behavior non-deterministic. + +## Solution + +Three-part fix: + +1. **Disable WP-Cron on page load** via `wp-config.php` +2. **Block external access to `wp-cron.php`** at the Traefik middleware +layer +3. **Deploy a Supercronic sidecar container** that calls wp-cron on a real +schedule via the internal Docker network + +## Implementation + +### 1. wp-config.php + +```php +define('ALTERNATE_WP_CRON', false); +define('DISABLE_WP_CRON', true); +``` + +`wp-config.php` is bind-mounted from the host so Ansible can manage it: + +```yaml +volumes: + - wordpress_data:/var/www/html + - ./wp-config.php:/var/www/html/wp-config.php +``` + +To pull a fresh copy out of a running container: +```bash +docker cp wordpress:/var/www/html/wp-config.php +/opt/docker/wordpress/wp-config.php +``` + +### 2. Traefik middleware — block external `wp-cron.php` + +```jinja +- traefik.http.routers.wordpress.middlewares=www-redirect,block-wpcron +- traefik.http.middlewares.block-wpcron.redirectregex.regex=^/wp-cron\.php.* +- traefik.http.middlewares.block-wpcron.redirectregex.replacement=/ +``` + +The router definition must include `block-wpcron` in the middlewares chain +or Traefik silently does not apply it. + +Verify: +```bash +curl -Lv https://plantbasedsoutherner.com/wp-cron.php 2>&1 | grep -E +"HTTP|Location" +# Expected: HTTP/2 302 + Location: https://plantbasedsoutherner.com +``` + +### 3. Supercronic sidecar + +Project structure: +``` +/opt/docker/wpcron/ + Dockerfile + crontab + docker-compose.yml +``` + +`crontab` (set to every minute — Wordfence and Jetpack schedule events at +this frequency): +``` +* * * * * curl -s http://wordpress/wp-cron.php?doing_wp_cron +``` + +`Dockerfile`: +```dockerfile +FROM alpine:latest + +ARG SUPERCRONIC_VERSION=v0.2.29 +ARG SUPERCRONIC_ARCH=amd64 + +RUN wget -q +https://github.com/aptible/supercronic/releases/download/${SUPERCRONIC_VERSION}/supercronic-linux-${SUPERCRONIC_ARCH} +\ + -O /usr/local/bin/supercronic && \ + chmod +x /usr/local/bin/supercronic && \ + apk add --no-cache curl + +COPY crontab /etc/crontab +CMD ["/usr/local/bin/supercronic", "/etc/crontab"] +``` + +The sidecar lives in its own compose project. To reach the WordPress +container, the wpcron compose file declares the WordPress network as +`external: true`. + +## Bonus Fix — Redis Auth on Production + +Discovered during the deploy that the production WordPress container was +missing the Redis password env var. Env vars had been moved to an `.env` +file on staging but the change never made it back to production. Fixed and +now part of the standard env handoff between environments. + +## Key Learnings + +- **Traefik `redirectregex` matches path only**, not the full URL — design +regex accordingly +- **`depends_on` only works within the same Docker Compose project** — for +cross-project dependencies, declare the network as `external: true` +- **`docker compose up -d --force-recreate `** is the right +command to pick up compose file changes (not `restart`, which reuses the +old config) +- **Cron interval matters:** Plugins like Wordfence and Jetpack schedule +per-minute events, so a 5-minute cron will leave them perpetually behind. +Every minute is the right default for this stack. + +## Status + +Deployed to staging then promoted to production. Running cleanly since. + + +...sent from Jenny & Travis \ No newline at end of file