Cron and Scheduling
How recurring fleet work is defined and scheduled across the CLI and the CaptainCore Manager plugin.
CaptainCore has no built-in scheduler. Recurring work is driven by the operating system’s cron on the server that runs the CLI. This page describes what the source shows about how those jobs are defined and what they are expected to do.
A note on precision: neither the CLI readme nor the CLI source contains example crontab lines for fleet jobs, so none are invented here. The only literal crontab entry anywhere in the source is the one the performance monitor installs on a customer site. Everything else below is described from the command definitions and from the configuration structure that holds the schedule.
captaincore cron
captaincore cron
captaincore cron reads the global configuration stored in the local database, looks for its scheduled_tasks key, and pretty-prints that array as JSON. It prints nothing if there is no configuration or no scheduled_tasks entry. In the current source it reports the schedule; it does not execute it.
A scheduled task record carries four fields:
{
"repeat_interval": "Daily",
"repeat_at": "9pm",
"command": "backup",
"target": "all"
}
repeat_interval is one of Hourly, Daily, Weekly, Quarterly, Biannually or Yearly. repeat_at is a time of day. command is the CaptainCore command to run and target names the site group. These records are edited in the CaptainCore Manager’s settings interface and reach the CLI through captaincore configuration sync or captaincore connect.
captaincore cron
captaincore configuration sync && captaincore cron
The nightly fleet sync
The commands designed for nightly whole-fleet use are the ones that take a target group, a --parallel flag and a --skip-if-recent flag. That combination is what makes a job safe to schedule and safe to re-run after a partial failure:
captaincore quicksave generate @all --parallel=10 --skip-if-recent=24h
captaincore backup generate @all --parallel=5 --skip-if-recent=20h
captaincore update @production --parallel=10 --skip-if-recent=24h
captaincore logs archive @all --parallel=10 --skip-if-recent=24h
captaincore sync-data @all
captaincore monitor run @production.monitor-on
captaincore capture generate @production
A few commands take a single site only and do not detect a target group themselves. Run those through the bulk command explicitly:
captaincore bulk usage-update @all --parallel=10
--skip-if-recent takes a duration such as 24h or 7d and skips any environment already processed inside that window, so an overlapping or re-run job costs almost nothing.
Two of these guard themselves against overlap directly. monitor run holds a PID lock at <path_tmp>/captaincore-monitor.lock and exits immediately if a previous run is still alive, emailing the admin address if it finds a stale lock left by a crashed run. Backup generation takes a per-site, per-environment lock file under the site’s data directory and skips a site another process is already backing up.
Long jobs are observable while they run:
captaincore progress
captaincore progress --clean
See running commands for how the bulk runner and progress work together.
Fleet mode in cron
On a multi-tenant server, adding --fleet runs a job once per captain ID in config.json, so a single crontab line covers every tenant:
captaincore backup generate @all --fleet
Without --fleet, jobs run against --captain-id, which defaults to 1.
Refreshing the local database
The CLI’s picture of the fleet comes from the CaptainCore Manager. Keeping it current is a scheduling concern of its own, because target resolution, drift and site list all read the local copy:
captaincore connect --sync
captaincore configuration sync
captaincore default-sync
captaincore provider sync
captaincore connect --sync reuses the saved token and API URL and prompts for nothing, which is what makes it suitable for cron.
Manager-side scheduled work
Not every recurring job lives in the CLI. The CaptainCore Manager plugin registers a set of WP-CLI commands under wp captaincore for scheduled and operational work. Its readme states these are typically run from cron on the fleet server:
dns, mailgun, remote, provider-sync, site-label, session-alerts, scheduled-reports, scan-queue, component-queue, update-queue, top-plugins, restic-cache, security-log-sizes, error-log-sizes, mu-manifest-generate and web-risk-check. The plugin also registers core-update-runs.
These run through WP-CLI against the Manager WordPress installation rather than through the CaptainCore binary, so a full fleet crontab usually mixes both:
captaincore quicksave generate @all --parallel=10 --skip-if-recent=24h
wp captaincore scan-queue --path=/path/to/manager/wordpress
The queue-shaped commands (scan-queue, component-queue, update-queue) are designed to be called repeatedly and drain a work queue a slice at a time, which suits a short interval rather than a nightly one.
Per-site cron installed by CaptainCore
One CaptainCore feature installs cron on the managed site itself. captaincore performance-monitor activate <site> deploys the sampling scripts and adds this entry to the site user’s crontab:
*/10 * * * * bash <private_dir>/php-monitor-check.sh
captaincore performance-monitor deactivate <site> removes both the scripts and the entry. See monitoring and health.
Writing your own crontab
Because the source does not publish a reference crontab, treat the following as shape rather than a copied configuration. Anchor jobs to the absolute path of the binary, set the config explicitly if it is not in the default location, and redirect output somewhere you will read it.
# m h dom mon dow command
0 2 * * * /usr/local/bin/captaincore quicksave generate @all --parallel=10 --skip-if-recent=24h >> /var/log/captaincore/quicksave.log 2>&1
Stagger jobs that compete for the same resources. Backup generation, quicksave backup and log archival all push through rclone to the same B2 account, and each holds a Restic repository lock per site, so running them concurrently at full parallelism gains little.