Scheduling Claude Agents with Hermes Cron: Daily Reports While You Sleep
A practical walk-through of Hermes's cron scheduler: YAML config, timezones, quota handling, and a worked example that posts a PR digest to Slack at 8am.
A practical walk-through of Hermes's cron scheduler: YAML config, timezones, quota handling, and a worked example that posts a PR digest to Slack at 8am.
Most Claude usage is interactive. You type, it responds, you iterate. But a lot of useful work is the opposite: a task you would like to run without you, every morning, forever. Hermes ships with a cron scheduler that was built for exactly that. You give it a natural-language task and a cron expression, and the Hermes daemon runs it in the background on the schedule you set.
This piece is a primer. We will look at how the scheduler is wired, where the files live, what execution context a scheduled task gets, and a concrete example that produces a daily pull-request digest.
~/.hermes/schedules/, one file per job or a combined schedules.yaml.~/.hermes/memory.The Hermes daemon watches ~/.hermes/schedules/ for YAML files. Each file describes one or more scheduled tasks. When the cron expression matches the current minute, Hermes spins up an agent session, feeds it the prompt, lets it run to completion, and logs the result.
A minimal schedule looks like this:
schedules:
- name: "morning-report"
cron: "0 8 * * *"
prompt: "Summarize last night's monitoring alerts and post to Slack #ops."
The cron expression is standard five-field syntax (minute, hour, day-of-month, month, day-of-week). The prompt is plain English — the same kind of instruction you would type into an interactive Hermes session. See docs for exact field names; some fields like timezone, model, or max_turns may be supported per-schedule to override the global defaults.
Here is a real task. Every morning at 8am, read yesterday's pull requests across a few repos, flag anything stuck in merge-conflict, and post a short summary to Slack #engineering.
schedules:
- name: "pr-digest"
cron: "0 8 * * 1-5"
prompt: |
For each repo in NousResearch/hermes-agent, NousResearch/hermes-skills,
and my personal list, look at pull requests updated in the last 24 hours.
For each PR:
- title, author, status
- whether it has merge conflicts
- whether CI is green
Write a short markdown summary grouped by repo and post it to Slack
channel #engineering using the slack tool. Keep it under 400 words.
max_turns: 15
max_budget_usd: 0.50
Two things to notice. First, 1-5 in the day-of-week field means weekdays only. Second, max_turns and max_budget_usd at the schedule level give you a hard cap so a runaway scheduled task cannot drain your account overnight.
This is the question that matters most and is easy to miss. When a cron task fires, does the agent start with a blank slate or with your memory and skills already loaded?
By default, scheduled tasks inherit the host Hermes environment — the same ~/.hermes/memory, the same skills, the same tool registry. That is usually what you want. The morning PR digest can recall that last week you flagged the same stuck PR, for example.
If you need isolation (an untrusted input, an experiment, anything where side-effects should not leak into your main memory), run the scheduled task in a separate terminal backend. Point it at a Daytona workspace or a Docker container and you get a clean room every time. We cover that pattern in deploying-hermes-on-daytona-ephemeral-environments.
Three things trip people up.
Timezone. Cron uses the system timezone of the host. If your VPS is in UTC and you live on the US east coast, 0 8 * * * fires at 3am local in winter and 4am in summer. Either set the host timezone with timedatectl set-timezone or compute the UTC offset yourself. Some scheduler configs support a per-schedule timezone field — check the docs.
Overlap. If a scheduled task takes longer than the gap between runs, the next run might start before the previous one finishes. For daily jobs this is rarely a problem. For */5 * * * * jobs it absolutely is. Design for the case where run N+1 sees artifacts from run N.
Quota. A scheduled task that silently burns tokens is the worst kind of bug. Always cap with max_budget_usd per schedule. Configure a fallback model chain so quota exhaustion on the primary (say, Claude Sonnet 4.6) downgrades to a cheaper model rather than failing loudly at 3am. Cost controls are covered in cost-control-hermes-max-turns-budget-fallback.
Cron scheduling only works if the Hermes daemon is actually running at the moment the cron fires. Three reasonable options:
What will not work: running cron jobs on your laptop and expecting them to fire when the lid is closed. The daemon has to be alive.
The Hermes daemon logs each scheduled run — start time, end time, tokens consumed, final status, and usually a truncated transcript. Logs live under ~/.hermes/logs/. Tail them during the first week to confirm your schedule actually fires when you expect.
For production schedules, send a short confirmation to a messaging gateway. A one-line "PR digest complete, 14 PRs reviewed, 2 flagged" in Telegram is a cheap health check. We cover gateway setup in hermes-messaging-gateway-telegram-discord.
The scheduler is the feature that turns Hermes from "a nicer CLI for Claude" into "a background agent that does useful work while you are not paying attention." Start with one schedule. Something low-stakes, something you would read if it existed. Confirm it fires. Then add more.
Massive collection of 148+ commands and 54 specialized agents for Claude Code. Covers development workflows, code review, testing, deployment, and team collaboration.
You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.
Use when executing implementation plans with independent tasks in the current session
Use when you have a spec or requirements for a multi-step task, before touching code