Scheduled tasks: Claude on a cadence
Claude Code works fine on a clock. The question is always the same: how much damage can it do between ticks, and how do you know when it did?
The two ways to schedule Claude
There are really only two:
- Cron + headless mode — boring, portable, works anywhere with a shell. Good up to about three concurrent jobs.
- The
/loopcommand— built into Claude Code itself. Lives inside an active session and fires at a fixed interval. Great for “while I'm working, also check X every 5 minutes.”
Cron recipe
# /etc/cron.d/claude-nightly
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
# every night at 02:47 local — run security sweep, open a PR if anything found
47 2 * * * deploy cd /srv/repo && \
git pull --ff-only && \
claude -p "$(cat ~/prompts/security-sweep.md)" \
--allowedTools "Bash(git:*),Bash(npm audit),Read,Grep" \
--max-turns 60 \
--max-budget-usd 3 \
--output-format json \
> /var/log/claude/sweep-`date +\%F`.json \
2> /var/log/claude/sweep-`date +\%F`.errFive rules for cron-scheduled Claude
- Pin the working directory.Cron's default CWD is
$HOME, which is almost never what you want. - Always
git pull --ff-onlyfirst. Start from a known tree. - Lock against overlap. Wrap the call in
flockor a PID file. Two overlapping Claude runs on the same repo will fight over the index. - Budget per run, not per day. A single runaway prompt can burn your daily budget in one tick.
- Log the JSON envelope.You'll want it when you diff two runs later.
# with flock — the version that doesn't eat its own tail
47 2 * * * deploy flock -n /tmp/claude-sweep.lock \
/usr/local/bin/claude-sweep.shThe /loop command
Inside an active Claude Code session, /loop 5m /my-prompt fires the prompt every five minutes for as long as Claude Code is running. It shares context with the session, which means it's fantastic for: “while I'm pairing on this bug, also poll the CI board and ping me when it turns green.”
It's not a replacement for cron. The session owns the loop, so closing Claude Code ends it. Use /loop for attended work, cron for unattended work.
The idempotency problem
Here's the thing nobody tells you about scheduled Claude Code: the same prompt, run twice, will not produce the same actions. It will notice a file it already refactored and refactor it differently. It will open a second PR because the first one “isn't quite right.” It will rediscover a stale comment and rewrite it.
Three ways to defuse this:
- State in the prompt. “If a PR with label
claude-sweepalready exists for this week, exit.” - State in the repo. Touch a sentinel file with a timestamp after each successful run and teach the prompt to read it first.
- State in the issue tracker. Which is really what you want — and which is where background agents come in.
When cron stops scaling
Cron is great for one prompt on one repo on one machine. The moment you want:
- Different prompts for different Linear issue types
- Routing work across multiple repos
- A human to approve risky edits mid-run
- Parallel runs without fighting over branches
- Audit trails for SOC 2
…you're describing a background agent. Keep going; that's the next guide.
Further reading
- Claude Code pricing — scheduled runs multiply fast; this is the budgeting formula.
- Claude Code --bare mode — pair with any scheduled invocation to eliminate hidden state across runs.
- Choose a permission mode for your CI —
dontAskis the right call for cron jobs. - Claude Code in GitHub Actions — GH's
scheduletrigger is the no-host alternative to cron. - Background agent on Linear — when your cron jobs multiply past three.
- Claude Code CLI reference — every flag, every command.
Cyrus handles the schedule AND the state.
Linear issue assignment is the schedule. Isolated worktrees are the lock. Audit trail and approvals come standard. No cron to maintain.
Try Cyrus free →