Why cron jobs run as webapp-svc, the generated format, and how to confirm a job actually ran.
sudo bosun add <app-name> writes /etc/cron.d/<app-name> automatically — fully commented out, so it has zero effect until you edit it. This doc covers the format, why it must run as webapp-svc, and how to confirm a job actually ran.
Standalone mode (no argus-tty): every
webapp-svcbelow is actually whatever account/etc/bosun.confnames (see standalone.md) — the cron filebosun addgenerates already has the right name baked in for your host, so copy its own example line rather than the literalwebapp-svcshown here.
bosun add generates# /etc/cron.d/myapp — scheduled tasks for 'myapp'.
#
# cron.d format has 6 fields, NOT 5 like a personal 'crontab -e':
# min hour dom month dow user command
#
# Run as 'webapp-svc' — the same unprivileged account puma/sidekiq already
# run as. It's the only account with a read grant on shared/.env, so any
# scheduled task that needs real app env vars loaded must run as this user
# via the generated shared/bin/run wrapper, not as root or a developer
# account.
#
# Example (uncomment and edit — runs daily at 02:30):
#30 2 * * * webapp-svc /var/www/myapp/shared/bin/run rake some:scheduled_task >> /var/www/myapp/shared/log/cron.log 2>&1
bosun add never overwrites this file on a re-run — same "leave existing content untouched" rule it applies to shared/.env/deploy.conf — so your edits are never clobbered.
/etc/cron.d/, not crontab -eA plain user's crontab -e has 5 fields (schedule + command) and is owned by that user. /etc/cron.d/* files are root-owned and need a 6th field — the user to run the command as — because one file can schedule jobs for multiple users. Using /etc/cron.d/<app> instead of webapp-svc's own crontab is deliberate:
webapp-svc is a nologin system account (see argus-tty's AT_WEBAPPS_SVC_USER) — it has no interactive shell for crontab -e to work cleanly with./etc/cron.d/ keeps it next to the app's other generated config (systemd units, deploy.conf) instead of hidden inside /var/spool/cron/.webapp-svc, specificallyshared/.env (the app's secrets — DATABASE_URL, SECRET_KEY_BASE, etc.) is root:root 600 with a single named-user ACL read grant for webapp-svc — no group can read it, deliberately (see the main README's "Why a per-app .env file" section). shared/bin/run (generated by bosun add alongside the cron file) sources deploy.conf and shared/.env, then execs your command under the app's RVM-resolved ruby/gemset. A cron job that doesn't run as webapp-svc calling this wrapper either can't read the env vars at all, or has to re-implement that loading itself — always prefer:
<schedule> webapp-svc /var/www/<app>/shared/bin/run <command> >> /var/www/<app>/shared/log/cron.log 2>&1
644 — bosun add already sets this.-/_ only, no dots, no ~. The app name validation bosun add already enforces (^[a-z][a-z0-9_-]{0,31}$) satisfies this./etc/cron.d/ on its own.sudo -u webapp-svc /var/www/<app>/shared/bin/run rake some:scheduled_task # run once by hand first
tail -f /var/www/<app>/shared/log/cron.log # the job's own output
journalctl -u cron -f # Debian/Ubuntu — follow live
grep CRON /var/log/syslog # Debian/Ubuntu — history