OpenClaw HEARTBEAT.md: Run a 24/7 Autonomous Agent on $5/Month
Learn how to configure OpenClaw HEARTBEAT.md to schedule background tasks, set activeHours, and run autonomous agents 24/7 without wasting tokens.
My OpenClaw agent used to sit idle for 22 hours a day. I paid for a capable AI and used it like a chatbot. Then I set up HEARTBEAT.md and it started doing real work while I slept — morning briefings, uptime checks, weekly code reviews. My monthly bill dropped to under $5.
This guide covers exactly how to set that up: what HEARTBEAT.md is, how to write the config, how to control when it runs, and three complete working setups you can copy directly.
Table of Contents
- What HEARTBEAT.md Is
- Basic Syntax
- Controlling Active Hours
- Three Background Task Examples
- Choosing the Right Model
- Session Isolation and Cost
- Three Complete Configs
- FAQ
- Checklist
What HEARTBEAT.md Is
HEARTBEAT.md is a workspace context file that gets injected into every heartbeat turn, plus an optional tasks: block for interval-based background checks.
The heartbeat is OpenClaw’s built-in periodic check-in mechanism — configured in openclaw.json, it fires on a set interval (e.g., every 30 minutes) and runs a brief agent turn in the background. HEARTBEAT.md is the file that tells the agent what to pay attention to during those turns.
There are two mechanisms for background work in OpenClaw:
- HEARTBEAT.md tasks — lightweight interval-based checks that piggyback on the heartbeat cycle. Good for “check every 30 minutes” patterns.
openclaw cron add— the proper scheduler for time-specific jobs (“run at 7 AM on weekdays”). Covered in the examples below.
Without HEARTBEAT.md, your agent only runs when you open a chat window. That means everything it could do proactively — monitoring, summarizing, reviewing — falls back on you to trigger manually.
The file lives in your OpenClaw workspace alongside MEMORY.md and SOUL.md. For ready-to-use agent personalities, browse the SOUL.md examples catalog.
Basic Syntax
A minimal HEARTBEAT.md with an interval task looks like this:
# Heartbeat Instructions
Check for anything urgent and respond if needed.
tasks:
- name: morning-digest
interval: 24h
prompt: "Fetch top 5 Hacker News stories, summarize each in 2 sentences, save to memory/morning-digest.md"Each task in the tasks: block has three fields:
| Field | Purpose | Example |
|---|---|---|
name | Unique task identifier | morning-digest |
prompt | Plain-English instruction for the agent | "Check uptime of URLs in MEMORY.md" |
interval | How often to run this check | 30m, 2h, 24h |
Tasks run based on their interval — there are no cron expressions or per-task model/timeout overrides. The model and timeout are set at the heartbeat level in openclaw.json.
You configure the heartbeat in openclaw.json under agents.defaults.heartbeat:
{
"agents": {
"defaults": {
"heartbeat": {
"every": "30m",
"isolatedSession": true,
"model": "anthropic/claude-haiku-4-5-20251001"
}
}
}
}every— how often the heartbeat fires (and checks which tasks are due)isolatedSession— each turn starts fresh, no accumulated conversation history (critical for cost)model— the model used for all heartbeat turns
OpenClaw will start scheduling on the next restart.
Controlling Active Hours
By default, the heartbeat fires at all hours. The activeHours setting lets you restrict when it is allowed to run. It lives under agents.defaults.heartbeat in openclaw.json.
Business hours only
{
"agents": {
"defaults": {
"heartbeat": {
"every": "30m",
"activeHours": {
"start": "08:00",
"end": "18:00",
"timezone": "Europe/Berlin"
}
}
}
}
}The heartbeat fires only between 08:00 and 18:00 Berlin time. Turns that would fire outside that window are skipped — they are not queued, just missed. activeHours supports start, end, and timezone (IANA identifier, or "user" / "local").
Weekday-only tasks
There is no weekendsOff field in activeHours. For tasks that should only run on weekdays, use openclaw cron add with a cron expression that targets Monday–Friday:
openclaw cron add \
--name "weekday-digest" \
--cron "0 8 * * 1-5" \
--session isolated \
--message "Prepare morning briefing." \
--model "anthropic/claude-haiku-4-5-20251001"The 1-5 in the day-of-week field means Monday through Friday only.
Always-on (24/7)
Omit activeHours entirely — that is the default, and it means the heartbeat fires on every tick with no hour restrictions:
{
"agents": {
"defaults": {
"heartbeat": {
"every": "30m",
"isolatedSession": true
}
}
}
}Use this for monitoring tasks that need to catch problems at any hour. You can also explicitly set a full-day window with { "start": "00:00", "end": "24:00" } if you want to be explicit.
Three Background Task Examples
1. Morning digest (time-specific → use openclaw cron add)
Runs at 7 AM on weekdays. Because HEARTBEAT.md tasks only support intervals — not specific times — this job needs openclaw cron add:
openclaw cron add \
--name "morning-digest" \
--cron "0 7 * * 1-5" \
--session isolated \
--message "Search for the top 5 AI news stories from the past 24 hours. Write a 3-sentence summary for each. Note any stories relevant to the projects in MEMORY.md. Save the full digest to memory/morning-digest.md with today's date as the heading." \
--model "google/gemini-3-flash-preview"The 1-5 in the day-of-week field means Monday through Friday only.
2. Uptime check (interval-based → use HEARTBEAT.md)
Checks every 30 minutes. This is a perfect fit for a HEARTBEAT.md task — it is interval-based, lightweight, and runs continuously:
tasks:
- name: uptime-check
interval: 30m
prompt: >
Check the HTTP status of each URL in MEMORY.md under the "Monitored URLs" section.
Log each result (URL, status code, response time) to memory/uptime-log.md.
If any URL returns a non-200 status, add an ALERT: prefix to that log line.Use a cheap model for this in the heartbeat config — it runs 48 times a day. Using anthropic/claude-sonnet-4-6 here would multiply costs for no benefit.
3. Weekly code review (time-specific → use openclaw cron add)
Runs every Sunday at 10 PM. Needs a specific time, so this is another cron job:
openclaw cron add \
--name "weekly-code-review" \
--cron "0 22 * * 0" \
--session isolated \
--message "Read the git log for the past 7 days from the repo in MEMORY.md. For each changed file, check for: missing error handling, hardcoded values, functions over 50 lines. Write a concise review to memory/weekly-review.md with: summary of changes, issues found, suggested improvements. Keep the total review under 500 words." \
--model "anthropic/claude-sonnet-4-6"Sonnet is appropriate here — code analysis benefits from stronger reasoning and it runs only once a week.
Choosing the Right Model
Model selection is the biggest lever for controlling cost. Match the model to what the task actually requires.
| Task type | Recommended model | Why |
|---|---|---|
| News fetch, web search | google/gemini-3-flash-preview | Pure retrieval, no complex reasoning |
| Simple monitoring | anthropic/claude-haiku-4-5-20251001 | Cheap, fast, handles pattern matching |
| File writing, summaries | anthropic/claude-haiku-4-5-20251001 | Handles structured output well |
| Code analysis | anthropic/claude-sonnet-4-6 | Stronger reasoning, worth the cost |
| Architectural review | anthropic/claude-opus-4-6 | Reserve for high-stakes weekly tasks |
A common mistake is setting every job to anthropic/claude-sonnet-4-6 because it gives better results. For a morning news digest, the quality difference between Gemini 3 Flash and Sonnet is minimal. The cost difference over a month is not.
Use the cheapest model that produces acceptable results for each task type. Model selection is the single biggest lever for keeping background task costs low.
Session Isolation and Cost
By default the heartbeat runs in the main session, accumulating conversation history. Enable isolatedSession: true in your heartbeat config so each turn starts completely fresh:
{
"agents": {
"defaults": {
"heartbeat": {
"every": "30m",
"isolatedSession": true
}
}
}
}Each isolated heartbeat turn starts fresh, executes the task, saves output to the memory directory, and closes. No state carries over between runs.
This has two implications for cost:
System prompt overhead. Every session loads your workspace files — MEMORY.md, SOUL.md, HEARTBEAT.md, and any daily logs. If these files are large, you pay for those tokens every single run. A 10-job daily schedule with a 5,000-token workspace runs 50,000 tokens of system prompt per day before the actual tasks start.
Frequency multiplies cost. A job that runs hourly runs 24 times per day, 720 times per month. A 1,000-token task at Haiku pricing costs roughly $0.0003 per run — $0.22/month. The same task on Sonnet costs roughly $0.003 per run — $2.16/month.
| Schedule | Runs/month | Haiku (1k tokens) | Sonnet (1k tokens) |
|---|---|---|---|
| Daily | 30 | ~$0.009 | ~$0.09 |
| Every 6 hours | 120 | ~$0.036 | ~$0.36 |
| Hourly | 720 | ~$0.22 | ~$2.16 |
| Every 15 min | 2,880 | ~$0.86 | ~$8.64 |
Keep workspace files lean. Use the cheapest model that produces acceptable results. Schedule high-frequency tasks less often if you can — checking uptime every 30 minutes instead of every 5 minutes cuts costs by 6x.
Three Complete Configs
These are ready-to-use setups for three different use cases. Copy the relevant one, replace the placeholder values, and add the heartbeat block to your openclaw.json.
Personal assistant
Time-specific jobs (morning briefing, evening wrap) use openclaw cron add. HEARTBEAT.md handles always-on interval checks.
# Morning briefing — weekdays at 8 AM
openclaw cron add \
--name "morning-briefing" \
--cron "0 8 * * 1-5" \
--session isolated \
--message "Check the weather for the location in MEMORY.md. List today's calendar events from MEMORY.md. Save the briefing to memory/daily-briefing.md." \
--model "anthropic/claude-haiku-4-5-20251001"
# Evening wrap — weekdays at 6 PM
openclaw cron add \
--name "evening-wrap" \
--cron "0 18 * * 1-5" \
--session isolated \
--message "Read today's entries in memory/daily-briefing.md. List any tasks marked incomplete in MEMORY.md. Write a short end-of-day note to memory/daily-briefing.md with status and tomorrow's top priority." \
--model "anthropic/claude-haiku-4-5-20251001"HEARTBEAT.md (for always-on interval checks):
# Heartbeat Instructions
Check for anything urgent and surface it.
tasks:
- name: urgent-check
interval: 2h
prompt: "Scan MEMORY.md for any items flagged urgent or overdue. If found, note them in memory/alerts.md."openclaw.json:
{
"agents": {
"defaults": {
"heartbeat": {
"every": "30m",
"isolatedSession": true,
"model": "anthropic/claude-haiku-4-5-20251001",
"activeHours": {
"start": "07:00",
"end": "19:00",
"timezone": "America/New_York"
}
}
}
}
}Developer
Time-specific jobs use openclaw cron add. The uptime monitor is interval-based and goes in HEARTBEAT.md.
# Dependency check — Mondays at 9 AM
openclaw cron add \
--name "dependency-check" \
--cron "0 9 * * 1" \
--session isolated \
--message "Read the package.json (or equivalent) from the repo path in MEMORY.md. Check for any packages with known security vulnerabilities using available tools. Log findings to memory/dependency-report.md with severity and recommended action." \
--model "anthropic/claude-sonnet-4-6"
# Weekly dev review — Fridays at 9 PM
openclaw cron add \
--name "weekly-dev-review" \
--cron "0 21 * * 5" \
--session isolated \
--message "Read git log for the past 7 days from the repo in MEMORY.md. Identify files changed most frequently. Flag any functions over 50 lines or files over 400 lines added this week. Write report to memory/weekly-dev-review.md." \
--model "anthropic/claude-sonnet-4-6"HEARTBEAT.md (interval uptime monitor):
# Heartbeat Instructions
tasks:
- name: uptime-monitor
interval: 30m
prompt: >
Ping each URL listed under "Monitored Services" in MEMORY.md.
Log status to memory/uptime-log.md.
If any service returns non-200, prepend ALERT to the log line.openclaw.json:
{
"agents": {
"defaults": {
"heartbeat": {
"every": "30m",
"isolatedSession": true,
"model": "anthropic/claude-haiku-4-5-20251001"
}
}
}
}Content creator
All three jobs are time-specific — they all use openclaw cron add.
# Content ideas — Mon/Wed/Fri at 7 AM
openclaw cron add \
--name "content-ideas" \
--cron "0 7 * * 1,3,5" \
--session isolated \
--message "Search for trending topics in the niche listed in MEMORY.md under 'Content Focus.' Generate 5 specific post ideas based on trending angles. For each idea, add: target keyword, estimated search intent, suggested headline. Append findings to memory/content-ideas.md with today's date." \
--model "google/gemini-3-flash-preview"
# Publish check — weekdays at 10 AM
openclaw cron add \
--name "publish-check" \
--cron "0 10 * * 1-5" \
--session isolated \
--message "Check the publishing schedule in MEMORY.md. If today has a scheduled post, add a reminder to memory/today.md with the post title and deadline. If the post draft exists in the drafts folder, note its word count and any incomplete sections." \
--model "anthropic/claude-haiku-4-5-20251001"
# Weekly stats — Sundays at 8 PM
openclaw cron add \
--name "weekly-stats" \
--cron "0 20 * * 0" \
--session isolated \
--message "Read memory/content-ideas.md for the past 7 days. Summarize: topics covered, top recurring themes, gaps in coverage. Write a brief weekly content strategy note to memory/weekly-strategy.md." \
--model "anthropic/claude-haiku-4-5-20251001"openclaw.json (heartbeat for always-on checking):
{
"agents": {
"defaults": {
"heartbeat": {
"every": "1h",
"isolatedSession": true,
"model": "anthropic/claude-haiku-4-5-20251001",
"activeHours": {
"start": "06:00",
"end": "22:00",
"timezone": "America/New_York"
}
}
}
}
}FAQ
Does the heartbeat run when my computer is off?
No. OpenClaw runs locally, so the heartbeat only fires when the OpenClaw process is running. If your machine is off at 7 AM, the morning digest job is skipped. For always-on schedules, you need OpenClaw running on a server or a machine that stays on.
What happens if a heartbeat turn runs too long?
The top-level agent timeoutSeconds applies to heartbeat turns just like interactive turns. If a task runs over the configured limit, OpenClaw terminates the session. For cron jobs, configure retry behavior in openclaw.json under cron.retry. For heartbeat tasks, the task simply misses that turn and runs again on the next interval.
Can I trigger the heartbeat manually without waiting for the next interval?
Yes. Use the CLI to inject a system event immediately:
openclaw system event --text "Run the uptime-check task from HEARTBEAT.md" --mode now--mode now fires the event in the next available turn. --mode next-heartbeat defers to the scheduled tick.
How do I see what a heartbeat turn did?
Check the session history in ~/.openclaw/agents/<agentId>/sessions/. Each heartbeat turn with isolatedSession: true creates its own session record you can inspect for output and token usage.
Can jobs communicate with each other?
Not directly — each job runs in isolation. The standard pattern is to write output to a shared file in the memory/ directory that other jobs can read. For example, the uptime-check job writes to memory/uptime-log.md, and a weekly summary job reads from it.
What is the cheapest setup that still works?
Use google/gemini-3-flash-preview for all retrieval and summarization tasks. Use anthropic/claude-haiku-4-5-20251001 for anything that needs a bit more structure. Reserve anthropic/claude-sonnet-4-6 for weekly code reviews or tasks where quality genuinely matters. A well-configured personal assistant setup with this model mix typically costs under $2/month.
OpenClaw HEARTBEAT.md Setup Checklist
-
agents.defaults.heartbeat.everyset inopenclaw.jsonto activate the heartbeat -
agents.defaults.heartbeat.isolatedSession: trueto prevent context accumulation -
agents.defaults.heartbeat.modelset to cheapest suitable model -
activeHoursconfigured if you want to restrict operating hours (start,end,timezoneonly — noweekendsOff) - HEARTBEAT.md
tasks:block usesname,interval,promptfields only - Time-specific jobs (e.g., “run at 7 AM”) created with
openclaw cron add, not HEARTBEAT.md - All model values use full
provider/modelformat (e.g.,anthropic/claude-haiku-4-5-20251001) - High-frequency tasks use Haiku or
google/gemini-3-flash-preview, not Sonnet - Tested a manual heartbeat trigger with
openclaw system event --text "..." --mode now - Session output checked in
~/.openclaw/agents/<agentId>/sessions/ - Workspace files (MEMORY.md, SOUL.md, HEARTBEAT.md) kept lean to reduce per-turn token cost
- Output paths for each task documented in MEMORY.md so you can find them
If you are running multiple agents and want them coordinated, OpenClaw Multi-Agent Setup covers the patterns.