Two-layer structure: Sources (raw notes) + Wiki (compile output) Four domains: Dev (40), Venture (3), Homelab (23), Reference (0) Includes CLAUDE.md spec, index pages at all levels, compile log Co-Authored-By: Lovebug <lovebug@herbylab.dev>
142 lines
3.7 KiB
Markdown
142 lines
3.7 KiB
Markdown
---
|
|
created: 2026-04-15
|
|
path: Sources/Homelab
|
|
project: wp-cron-supercronic-deploy
|
|
status: completed
|
|
tags:
|
|
- pbs
|
|
- wordpress
|
|
- traefik
|
|
- docker
|
|
- cron
|
|
type: session-notes
|
|
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 |