Scheduling Work — Cron, Triggers & Background Jobs
A Worker That Shows Up on Its Own
In the last lesson you built a tiny worker: claude -p, a single instruction, a result. But you still had to type the command. The next level of leverage is making that worker show up on its own — every morning, every hour, or the moment something happens.
This lesson is about the two ways jobs get started without you:
- Schedules — "run this every day at 7am."
- Triggers — "run this whenever X happens" (a file arrives, a form is submitted, a webhook fires).
Here's the most important thing to understand up front, so you never feel misled:
Claude Code does not have its own always-on scheduler that you set and forget. The scheduling comes from a tool outside Claude — your computer's built-in scheduler, or a cloud service — and that tool's only job is to run your
claude -pcommand at the right time.
In other words: the thinking is Claude's job. The timing is someone else's job. You wire them together.
Option 1: Cron — Your Computer's Built-In Alarm Clock
Every Mac and Linux machine ships with a scheduler called cron. You give it two things: a schedule and a command. It runs the command on that schedule, forever, as long as the machine is on.
You edit cron's list of jobs (the "crontab") by running crontab -e. Each line is one job. The schedule is written as five fields:
┌─────── minute (0–59)
│ ┌───── hour (0–23)
│ │ ┌─── day of month (1–31)
│ │ │ ┌─ month (1–12)
│ │ │ │ ┌ day of week (0–6, Sunday = 0)
│ │ │ │ │
0 7 * * * <your command here>
A * means "every." So 0 7 * * * means "at minute 0 of hour 7, every day" — i.e. 7:00am daily. A few you'll actually use:
| Schedule | Meaning |
|---|---|
0 7 * * * | Every day at 7:00am |
0 * * * * | Every hour, on the hour |
*/15 * * * * | Every 15 minutes |
0 9 * * 1 | Every Monday at 9:00am |
The catch: cron only runs while your computer is on and awake. A laptop that's asleep at 7am won't run the job. That's fine for many personal automations, but it's why the next two options exist.
Option 2: GitHub Actions — A Cloud Scheduler That's Always On
GitHub Actions is a free-for-most-uses service that runs commands in the cloud on a schedule — no machine of yours required. If you've already got a project in a GitHub repository, this is often the easiest "always-on" option.
You add a file describing the schedule and the steps. Here's a daily job, in GitHub Actions' YAML format:
name: Daily Summary
on:
schedule:
- cron: "0 7 * * *" # same 5-field cron syntax, in UTC
workflow_dispatch: # also lets you run it manually with a button
jobs:
summarize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Claude
run: claude -p "Summarize this week's changes" --allowedTools "Read"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Two things to notice. The cron: line uses the exact same five-field syntax you just learned (handy — learn it once). And the ANTHROPIC_API_KEY is pulled from a secret, not pasted in plain text — never put keys directly in a file you commit. (Anthropic also publishes an official Claude Code GitHub Action, anthropics/claude-code-action, designed specifically for reacting to pull requests and issues — worth knowing if your work lives on GitHub.)
Cloud schedulers from other providers (AWS, Google Cloud, etc.) work the same way conceptually: a managed clock that fires your command. GitHub Actions is just the friendliest starting point.
Triggers: Running on an Event, Not a Clock
A schedule answers "when?" A trigger answers "when something happens." Same idea — an outside system starts your claude -p job — but the starting gun is an event rather than a time.
| Trigger type | Fires when… | Example |
|---|---|---|
| Webhook | Another service sends a signal to a URL | A new lead submits your form → summarize and notify you |
| File change | A file appears or changes in a folder | A new invoice PDF lands → extract the total and log it |
| Inbound message | An email or chat arrives | A support email comes in → draft a reply for review |
The pattern never changes: something external detects the event, then runs your Claude command, optionally piping the event's data in. For instance, a webhook handler might receive form data and run:
echo "$FORM_DATA" | claude -p "Summarize this lead in 2 sentences and rate fit 1-10" --output-format json
You don't need to master webhooks today. The point is to recognize the shape: schedules and triggers are the same machine with a different "go" button.
Worked Example: A Daily Project Digest
Let's build a real, end-to-end scheduled job: every morning at 8am, Claude reviews yesterday's activity in a project and writes you a short digest.
Step 1 — Get the headless command right (run it by hand first).
Always prove the command works manually before you schedule it. You don't want to discover a typo three days later when you wonder why no digests arrived.
cd ~/projects/my-app
git log --since="1 day ago" | claude -p "Write a 5-bullet daily digest of what changed: features, fixes, and anything that looks risky. Keep it skimmable." --allowedTools "Read" > ~/digests/$(date +%Y-%m-%d).txt
This pipes yesterday's commits into Claude, asks for a digest, and saves it to a dated file. Run it once. Read the output. Tweak the prompt until you like it.
Step 2 — Wrap it in a small script.
Cron lines get messy. Put the command in a file, say ~/scripts/daily-digest.sh, so the schedule just calls one clean thing.
Step 3 — Schedule it. Run crontab -e and add:
0 8 * * * ~/scripts/daily-digest.sh
That's it. Every day at 8am, while you're getting ready, Claude reads the project and leaves you a digest. (In Lesson 3 we'll add the final touch: instead of saving to a file you have to open, it'll message you the digest directly.)
Two Rules That Save You Pain
Rule 1: Test manually before you schedule. A scheduled job runs silently. If it's broken, it fails silently too. Always run the exact command by hand first.
Rule 2: Make failures loud. A digest that quietly stops arriving is worse than one that errors visibly. Have your job log its output somewhere, or — better — notify you when it runs and when it fails. We'll cover exactly how in the next two lessons.
Summary
- Claude Code has no always-on scheduler of its own — you pair a
claude -pcommand with an outside scheduler that decides when it runs. - Cron is the built-in scheduler on Mac/Linux: a five-field schedule plus a command, run via
crontab -e. It only runs while your machine is on. - GitHub Actions (and other cloud schedulers) run jobs in the cloud, always on, using the same five-field cron syntax. Keep API keys in secrets.
- Triggers start a job on an event (webhook, file change, inbound message) instead of a clock — same machine, different "go" button.
- Build the pattern in order: get the headless command working by hand, wrap it in a small script, then schedule it.
- A silent schedule fails silently — always test manually first and make failures loud.