Cron that shows up.
Every run, its own machine.
Attach a cron expression to a function and it fires on time — no runner to babysit, no shared box collecting state between jobs. Every scheduled run executes in a fresh isolated microVM, exactly like a manual invoke.
Write cron the way you already know it.
Standard 5-field expressions — minute, hour, day of month, month, day of week. */5 * * * * just works, and the finest granularity is one minute. No custom DSL, no rate() dialect to translate in your head.
The five fields, unchanged
A schedule is a cron string attached to a function id. Deploy the function once, attach the expression, and it fires until you pause it — change cadence later with a single update, no redeploy of the underlying code.
# ship the function once
pandastack function deploy handler.py \
--name daily-report --runtime python
# attach a cron — every day at 09:00 UTC
pandastack schedule create --name daily-report \
--fn <function-id> --cron "0 9 * * *"A fresh microVM answers every tick.
Cron on a shared runner means one job's leftovers become the next job's bug. Here, every scheduled run executes the target function in a fresh isolated Firecracker microVM — the same path a manual invocation takes.
Fresh runtime, every run
No shared state, no noisy neighbours. Each run boots its own machine, does its work, and disappears — nothing accumulates between ticks.
VM-level isolation
Scheduled code runs behind hardware virtualization, not a container namespace. kill -9 can't escape the sandbox.
Golden metrics included
Every invocation lands in ClickHouse automatically — p50/p95/p99 latency, error rate, cold-start rate — scheduled runs included.
Operate it like software, not crontab.
A schedule is a record you can read and act on — cron, paused, last_run_at — not a line in a file on a box somewhere. Pause without deleting, trigger on demand, audit every run through the API.
Test now, not at 09:00
Manual triggers run the cron-backed workflow immediately, so you never wait for the next window to find out a job is broken. Pausing keeps the definition and stops future automatic invocations until you resume — the schedule and its history stay put.
# pause without deleting; resume picks the cron back up
pandastack schedule pause <schedule-id>
pandastack schedule resume <schedule-id>
# run it right now, then read the history
pandastack schedule trigger <schedule-id>
pandastack schedule runs <schedule-id>
# change cadence without touching the function
pandastack schedule update <schedule-id> --cron "0 */6 * * *"Ship a bundle once, schedule it forever.
Schedules drive PandaStack Functions — Python or Node.js bundles stored in GCS, versioned on every deploy. You can even pre-register a function with no code, attach the schedule immediately, and it starts firing the moment the first bundle lands.
Ship on the millisecond cloud.
Free tier with $5.40/mo usage credit. No card. Apache-2.0.