Automating a Monthly Meal Planner with OpenClaw Cron — A Beginner's Step-by-Step
meal.vibed-lab.com regenerates itself every month with zero clicks. Here is the exact, beginner-friendly recipe for setting up an OpenClaw cron job — the trigger, the skill, the test loop, and the patterns that make it actually trustworthy.
More in Guides
- Your Claude Code Skill Won't Trigger? The Description Is Doing 90% of the Work
- Why one benchmark won't tell you the best coding LLM in 2026 — and which three together actually do
- Designing Frontends Claude Can Actually Use — A 7-Step Field Guide From the Day My Scoring App Got Audited by Its Own AI
- Stop AI from Fabricating Research Citations: A Build-Pipeline Checklist
- I Combined Two Open-Source Repos Into an AI That Plans, Builds, and Reviews Its Own Code
Every month, meal.vibed-lab.com quietly rebuilds itself. On the 25th, next month's family meal plan is generated, new draft recipes are written, the data is synced into the site repo, the site is rebuilt, pushed to GitHub, deployed by Cloudflare Pages, and a Telegram message lands in my pocket saying it's done. I don't touch anything.
That whole loop is driven by OpenClaw cron. This post is the recipe — written for someone who has never set up a cron job before. We'll build up the mental model, walk the exact commands step by step, and use the meal planner as the running example so every abstract idea has something concrete attached to it.
🎯 What "automated" actually means here
Before any tooling, it helps to name the loop we're automating. Once a month, the meal planner does this on its own:
- Archive the current month's plan.
- Load the family profile, holidays, and the existing recipe pool.
- Generate next month's plan + any new draft recipes.
- Validate the plan against nutrition/variety thresholds (and retry if it fails).
- Write the result into the Obsidian vault (the source of truth).
- Sync the vault into the website's data folder.
- Build the site, commit, and push to
main. - Cloudflare Pages auto-deploys the push.
- Send a Telegram summary.
The word "automated" is doing a lot of work there. The hard part isn't running something on a schedule — it's running something unattended that you can trust to either succeed correctly or fail loudly without leaving a mess. We'll get to that. First, the trigger.
🧩 The two pieces: a cron job and a skill
The single most useful thing to understand up front is that OpenClaw automation is two separate concerns:
- The cron job answers when and what to kick off. It's a tiny schedule + a message. That's it.
- The skill answers what actually happens. It's a documented procedure (in my case, a
SKILL.mdplus a folder of small scripts) that the agent follows.
Keeping these separate is what makes the whole thing maintainable. The cron job almost never changes. The skill — the real logic — lives in version control where you can read it, test it, and improve it. The cron job just says: "at this time, wake up an agent and tell it to go run that skill."
For the meal planner, the cron job's entire payload is one sentence:
monthly-meal-planner: generate next month's plan.
Follow SKILL.md phases 0–9. target_month = next month.Everything else — the 9 phases, the validation, the git push — lives in the skill. The cron is just the alarm clock.
🪜 Step by step: your first OpenClaw cron job
Here's the full path from nothing to a working scheduled job. Every step shows the general form first, then the exact meal-planner value.
Step 1 — Decide the schedule (the "cron expression")
A cron expression is five fields: minute hour day-of-month month day-of-week. A few you'll actually use:
0 9 * * * every day at 09:00
0 9 * * 1 every Monday at 09:00
0 9,15,21 25 * * the 25th of every month, at 09:00, 15:00, and 21:00
*/15 * * * * every 15 minutesThe meal planner uses 0 9,15,21 25 * *. Why three times on the 25th instead of once? Resilience. If the machine is asleep or the network hiccups at 9am, the 3pm or 9pm run still catches it — and the job is built to do nothing if the month is already done (more on that under idempotency). Picking a schedule with a built-in retry window is a beginner-friendly habit that saves you from "the one day it didn't fire."
💡 Timezones matter. Cron itself is timezone-naive; OpenClaw lets you pin one explicitly. Always set it, or your "9am" will drift to whatever the server thinks 9am is. The meal planner pins
Asia/Seoul.
Step 2 — Decide what the wake-up does (the payload)
When the cron fires, OpenClaw starts an agent turn and hands it a message. You have two broad styles:
- Inline instruction — put the whole task in the message ("summarize today's unread email and send it to me"). Good for small one-shot tasks.
- Skill trigger — point the agent at a skill it should follow. Good for anything with more than ~3 steps, anything that touches files or git, or anything you'll want to test and evolve.
The meal planner is firmly in the second camp. Its message just names the skill and the variable input (target_month = next month). The actual procedure is the SKILL.md the message refers to.
One more decision: which session. Use --session isolated for scheduled jobs. An isolated session means the cron run gets its own clean context instead of barging into your main chat history. For unattended automation you almost always want isolated.
Step 3 — Create the job
The command is openclaw cron add. Here's the meal planner, annotated:
openclaw cron add \
--name "monthly-meal-planner" \
--cron "0 9,15,21 25 * *" \ # Step 1: the schedule
--tz "Asia/Seoul" \ # pin the timezone
--session isolated \ # Step 2: clean, unattended context
--message "monthly-meal-planner: generate next month's plan. Follow SKILL.md phases 0–9. target_month = next month." \
--timeout-seconds 1800 \ # give long jobs room (30 min here)
--announce --channel telegram --to "<your-chat-id>" # Step 5: where the summary goesA few flags worth knowing as a beginner:
--timeout-seconds— the default timeout is short. A job that builds and deploys a site needs minutes, not seconds. The meal run averages ~17 minutes, so it's given 30.--every <duration>— if you want "every 10 minutes / every 2 hours" instead of a cron expression, use this instead of--cron.--at <when>+--delete-after-run— for a one-shot ("run this once tomorrow at 3pm, then delete yourself").--disabled— create it switched off, so you can inspect it before it ever fires.
Step 4 — Test it before you trust it
This is the step beginners skip and regret. Never wait for the real schedule to find out if your job works. Fire it manually:
openclaw cron list # find the job id
openclaw cron run <job-id> # run it right now, on demandThe meal planner goes one better with a dry run. Its ship script honors a DRY_RUN=1 flag that does everything except the final git push — so you can watch the entire pipeline run end to end without actually publishing:
DRY_RUN=1 openclaw cron run <job-id>Then inspect what happened:
openclaw cron runs <job-id> # run history (success/fail, duration)
openclaw cron status # scheduler healthDesigning your skill so it has a safe dry-run mode is one of the highest-leverage things you can do for an unattended job.
Step 5 — Get told what happened
A cron you can't see is a cron you can't trust. The --announce --channel telegram --to <id> flags above mean every run ends with a summary delivered to me. The meal planner's success message is deliberately information-dense:
✅ 2026-06 meal plan generated
meals: 98
new draft recipes: 0
live: https://meal.vibed-lab.com/2026-06/
sha: <git commit>The live URL and commit sha in the message mean I can verify the result in two taps without opening a terminal. Pick a delivery channel you actually check.
Step 6 — Operate it day to day
The commands you'll reach for after it's live:
openclaw cron list # everything you've scheduled
openclaw cron disable <job-id> # pause without deleting
openclaw cron enable <job-id> # resume
openclaw cron edit <job-id> ... # patch fields (schedule, message…)
openclaw cron rm <job-id> # deleteThat's the whole lifecycle. If you stopped reading here, you could schedule a real job. But scheduling it is the easy 20%. The next section is the 80% that decides whether you trust it at 9am on the 25th while you're asleep.
🛡️ Making a cron you can actually trust
An unattended job that mostly works is worse than no job at all, because it fails silently and you find out three weeks later. These are the patterns the meal planner uses — and they generalize to any serious automation.
1. Idempotency — running twice must be safe. Remember the schedule fires three times on the 25th. So the very first thing the skill does is check a marker file: if {month}-done exists, it exits immediately and silently. The second and third firings are no-ops. Rule of thumb: design every scheduled job so that running it twice does no harm.
2. Fail loud, never partial. Every phase has an explicit exit code and a named failure. A broken nutrition file exits with LOAD_INPUTS_FAIL; a failed site build exits with BUILD_FAILED. Crucially, the job never commits half a result — it either completes the whole pipeline or aborts cleanly. And before it aborts, it enqueues a Telegram alert. Silent failure is the enemy; a loud failure you can act on.
3. A validation gate with bounded retries. The generated plan is checked against thresholds (nutrition within ±15% of targets, variety, no weekday-breakfast entries). If it fails, the agent regenerates and re-validates — up to 3 times. After three strikes it "dead-letters" the bad output to a review folder and alerts me, instead of shipping something wrong. Automation should be allowed to retry, but never allowed to loop forever.
4. One source of truth. The Obsidian vault is canonical; the website's data folder is derived from it by a one-directional sync. There is no scenario where the site and the vault disagree and you have to guess which is right. If your automation writes to two places, make one of them the boss.
5. No fabricated numbers. This is a content rule, but it's enforced structurally: the planner is forbidden from inventing nutrition figures, and new recipes land as review-gated drafts that never auto-promote to "active." The automation does the tedious 90%; a human still signs off on the part where being wrong matters.
6. The build is part of the job. Before pushing, the skill runs the site's production build as a smoke test. If the build breaks, the job aborts before the push — so a bad generation can never take the live site down. Deploy only what you've proven compiles.
None of these are exotic. They're just the difference between "I wrote a script" and "I trust this script to run while I sleep."
🍱 The meal planner, end to end
Putting it together, here's what one firing actually does — the cron message kicks off a single agent turn that walks nine phases:
preflight (mounts, clean repo, marker checks) → archive last month → load inputs from the vault → build slots for every meal of the month → generate the plan + draft recipes → validate (retry ≤3) → write into the vault → sync vault → repo → build + ship (smoke build, commit, push) → notify over Telegram. Cloudflare Pages sees the push to main and deploys it.
The split is the whole trick: the cron job is one line and never changes; the skill is a readable, testable, version-controlled procedure that I improve over time. The schedule is dumb on purpose. The intelligence lives where I can see it.
✅ Takeaways
If you're setting up your first OpenClaw cron, the short version:
- Separate the when (cron job) from the what (skill). Keep the cron trivial.
openclaw cron addwith--cron,--tz,--session isolated,--message,--timeout-seconds, and an--announcedelivery target.- Test with
openclaw cron run(and aDRY_RUNmode) long before the real schedule. - Engineer for unattended trust: idempotency markers, loud failures, bounded retries, a single source of truth, and a notification on every outcome.
The meal planner has been regenerating itself this way every month — generate, validate, build, deploy, notify — without me in the loop. The first version took an afternoon to wire up. The reliability patterns took longer, and they're the part that actually matters.
Written by
Jay Lee
Korea-Licensed Pharmacist (#68652) · Senior Researcher
Korea University, College of Pharmacy (B.S. + M.S., drug delivery systems & industrial pharmacy). Building production-grade AI tools across medicine, finance, and productivity — without a CS degree. Domain expertise first, code second.
About the author →Related posts