Skip to content

Cron Jobs & Background Tasks

Measured on the dev VM, 2026-09-25; the alerter and backup rows were added 2026-10-10:

UnitKindWhat it does
logrite-deploy-watcherservice, always onPolls each branch tip every 60s, deploys through the console, reports to Discord
logrite-managementservice, always onThe console at management.logrite.ai
logrite-reportsservice, always onStatic report pages, Tailscale-reachable
logrite-status-reporttimer, hourlyRoll-up of every service, the host and anything that has drifted. Since 2026-10-10 it also warns when the host’s OpenSearch ML embedding model is not DEPLOYED (e.g. DEPLOY_FAILED)
logrite-internal-plane-alert@dev, @prodtimer, every 10 minAnnounces /internal/mcp/** auth refusals on that host — see below
logrite-disk-guardtimer, hourlyTrims build cache unused for 48h (-af), removes dangling images and exited containers (never --preadopt- backups), trims dead scan checkouts. Old pipeline images are the console janitor’s, daily
actions.runner.*8 servicesSelf-hosted GitHub Actions runners, one per repo
logrite-backup (prod VM)timer, nightly 02:30Prod snapshot with logical database dumps — see below
Terminal window
systemctl list-units --type=service --all | grep -i logrite
systemctl list-timers --all | grep -i logrite
journalctl -u logrite-deploy-watcher -f

Internal-plane alerter and nightly backups (2026-10-10)

Section titled “Internal-plane alerter and nightly backups (2026-10-10)”

Documented from LogriteMasterControl main at 9ce9a1fa.

Internal-plane alerter (logrite-internal-plane-alert@<host>.timer, every 10 minutes per host). It reads the backend’s /internal/mcp/** authentication refusals and announces them to Discord when at least LOGRITE_INTERNAL_ALERT_THRESHOLD (default 5) fall inside a rolling LOGRITE_INTERNAL_ALERT_WINDOW_MIN (default 10) minute window — so 4 refusals in one run and 4 in the next still cross the threshold once. It records what it has seen only after a delivered announcement (or when below the threshold); a failed send exits 1 and leaves the state untouched, so the next run tries again. The unit runs the installed copy in /opt/logrite-ops/security/ (installed by ops/security/install.sh), and its --self-test checks the backend repository’s origin/main with git grep, not a working tree on whatever branch is checked out.

Nightly prod backup (logrite-backup.timer on the prod VM, OnCalendar=*-*-* 02:30:00, RandomizedDelaySec=600, Persistent=true), installed as root by ops/backup/install_nightly.sh. Each run of ops/backup/prod_snapshot.sh writes a snapshot directory under /home/deployd/logrite-backups/ with volume copies and consistent logical dumps (logical/postgres-all.sql.zst, logical/mongo.archive.gz, logical/redis-dump.rdb) and a log in meta/snapshot.log. Containers are found by their compose service label first, then the Coolify label; a database with no running container is recorded as a failure rather than skipped. The last 7 snapshots are kept, and pruning runs only after a successful snapshot.

Terminal window
systemctl list-timers | grep -E 'logrite-(backup|internal-plane-alert)'

Everything below ran on the host destroyed on 2026-08-03. None of these files, processes or crontab lines exist. They are kept because old runbooks, tickets and Discord threads still refer to them by name — each one names what answers that question today. Nothing here is safe to copy and run.

Open the pre-2026-08-03 cron and PM2 runbooks

Replaced by: the hourly logrite-status-report, which warns when the model is not DEPLOYED (2026-10-10). The script itself was deleted from the repository.

Location: /root/logrite/cli/model-health-check.js Schedule: Every 30 minutes Purpose: Verify the OpenSearch ML embedding model is loaded and responsive

  1. Calls the Semantic Search API /health endpoint
  2. Checks if the ML model responds to a test embedding request
  3. If model is unloaded: calls opensearch-mgr model-ensure
  4. If model fails to reload: sends Discord alert
  5. Logs result to audit log
Terminal window
node /root/logrite/cli/model-health-check.js
*/30 * * * * node /root/logrite/cli/model-health-check.js >> /var/log/logrite/model-health.log 2>&1

Replaced by: logrite-status-report.timer (hourly). It reads the git catalogue, not PM2 and not Coolify.

Location: /root/logrite/cli/status-reporter.js Schedule: Daily at 09:00 UTC Purpose: Send proactive status reports to Discord

  1. Queries PM2 for all Logrite process statuses
  2. Checks OpenSearch cluster health
  3. Checks semantic search model status (via semantic-status)
  4. Builds a formatted Discord embed
  5. Posts to Discord webhook
Terminal window
node /root/logrite/cli/status-reporter.js
# Or via master CLI:
./cli/logrite-manager.js notify-status
0 9 * * * node /root/logrite/cli/status-reporter.js >> /var/log/logrite/status-reporter.log 2>&1

Replaced by: ops/management/notifier.py — the component that performs the deploy also announces it, so the two cannot disagree.

Location: /root/logrite/cli/deployment-notifier.js Runtime: Always running (PM2 daemon) Purpose: Listen for PM2 events and send Discord notifications

  • Connects to PM2 event bus
  • Listens for start, stop, restart, exit, error events
  • Sends color-coded Discord notifications for each event
  • Enriches notifications with service name, branch, port, and uptime
Terminal window
pm2 start /root/logrite/cli/deployment-notifier.js --name deployment-notifier
pm2 save # Persist across reboots
PM2 EventDiscord MessageColor
start🚀 Service StartedGreen
restart🔄 Service RestartedBlue
stop⏹️ Service StoppedYellow
exit⚠️ Service ExitedOrange
error🚨 Service ErrorRed

Location: /home/victor/logrite/projects/JobSearch/generate-traffic.sh (Victor server only) Schedule: Every 15 minutes Purpose: Keep fresh runtime logs/traces flowing into OpenSearch for the JobSearch demo app — without anyone hitting URLs by hand — so Cortex always has recent errors to surface and offer Fix it on.

  1. Probes http://localhost:9081/ and exits quietly (exit 0) if the app is down (e.g. between deploys/reboots) — never errors the cron.
  2. Normal traffic — GET /jobs/search?term=… across 8 terms (spring, java, python, react, devops, kubernetes, terraform, aws), plus /jobs, /employers, /candidates/search?term=engineer. Populates logs + traces with successful queries.
  3. Error traffic — hits the app’s simulate-error endpoints (/candidates/simulate-error, /jobs/simulate-error, /employers/simulate-error, /proposals/simulate-error, /jobs/simulate-search-error), each throwing a real, traceable exception (Arithmetic, NumberFormat, NullPointer, IndexOutOfBounds, ClassCast) so a fresh, varied set of SEVERE logs stays in the recent window, each tracing to a real class/method for the Fix-it levels.
  4. Appends <timestamp> generate-traffic: ok to traffic.log.

Cortex’s deterministic error sweep reads these SEVERE logs and renders a summary + Fix it button. See AI Service → Deterministic error sweep.

Terminal window
/home/victor/logrite/projects/JobSearch/generate-traffic.sh
*/15 * * * * /home/victor/logrite/projects/JobSearch/generate-traffic.sh >> /home/victor/logrite/projects/JobSearch/traffic.log 2>&1

To remove the feature entirely: delete the crontab line and the script.

Write a systemd unit and a timer; do not add a crontab entry. A unit gets you logs in journalctl, a recorded exit status, and a failed state something can alert on — a cron line gets you an email nobody reads.

/etc/systemd/system/logrite-example.service
[Unit]
Description=LogRite example task
[Service]
Type=oneshot
WorkingDirectory=/opt/logrite-ops
ExecStart=/usr/bin/python3 /opt/logrite-ops/example.py
/etc/systemd/system/logrite-example.timer
[Unit]
Description=Run the LogRite example task hourly
[Timer]
OnCalendar=hourly
Persistent=true # catch up a run missed while the box was down
[Install]
WantedBy=timers.target
Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now logrite-example.timer
sudo systemctl start logrite-example.service # prove it works before waiting an hour
TaskWhere it lives nowSchedule
Deploy watcherlogrite-deploy-watcher.servicecontinuous, 60s poll
Management consolelogrite-management.servicecontinuous
Hourly status roll-uplogrite-status-report.timerhourly
Disk guardlogrite-disk-guard.timerhourly
Model health checkgone — the console’s overview answers this continuously—
Deployment notifiergone — deploy notifications are emitted by the thing that performs the deploy (ops/management/notifier.py), so they cannot outlive a control plane being removed—