Cron is easy until it has to be reliable
Writing a cron expression is trivial. Running scheduled work *reliably* in production is not, because real schedulers have to answer hard questions:
- What happens if a run fails? Is there a retry? With backoff?
- What if a run takes longer than the interval — do you get overlapping executions?
- How do you know a job didn't run at all (the silent failure)?
- Is execution exactly-once, or can a job fire twice across replicas?
- Where do the logs go, and how do you alert on failure?
A single crontab line on one box answers none of these well. That's why dedicated scheduling matters.
Two kinds of cron hosting
| Type | What it does | Examples |
|---|---|---|
| HTTP cron / monitors | Pings a URL on schedule; alerts if it fails | cron-job.org, EasyCron, Cronitor (monitoring) |
| Compute cron | Runs your code/container on schedule | GitHub Actions schedule, Cloud Scheduler + Run, Kubernetes CronJob, PandaStack cronjobs |
HTTP cron triggers an endpoint your app exposes. Compute cron runs your actual job code in its own environment. They solve different problems — and combining a compute scheduler with a *monitor* gives you both execution and dead-man's-switch alerting.
The platforms
cron-job.org / EasyCron (HTTP cron)
Simple, often free, and great for "hit this URL every N minutes." Pair with a health monitor. The limitation: your job logic has to live behind an HTTP endpoint, and you must secure that endpoint so randoms can't trigger it.
Cronitor / Healthchecks.io (monitoring)
These don't run your jobs — they *watch* them. Your job pings a URL when it starts and finishes; if the ping doesn't arrive on schedule, you get alerted. This is the answer to the "how do I know it *didn't* run" problem, and it's the piece most homegrown cron setups lack.
Google Cloud Scheduler + Cloud Run
A robust compute-cron pattern: Cloud Scheduler fires on schedule and invokes a Cloud Run job/service. Managed, reliable, with retries and logging. Great if you're on GCP. AWS EventBridge Scheduler + Lambda/Fargate is the equivalent.
GitHub Actions scheduled workflows
Convenient if your job lives in a repo and is occasional. Caveats worth knowing: scheduled Actions can be delayed under load and aren't guaranteed to run exactly on time, so they're not ideal for strict-SLA jobs. Fine for nightly maintenance, not for minute-precise tasks.
Kubernetes CronJob
The primitive under many platforms. Powerful (concurrency policies, history limits, retries) but you own the cluster. Good if you already run Kubernetes.
PandaStack
Disclosure: my platform — and cronjobs are a first-class app type, not an afterthought. You define a schedule and PandaStack runs your job as a containerized execution (built the same way as your apps: rootless BuildKit, deployed on K8s), with live logs you can inspect per run. Because it runs your actual code in a container, you're not limited to pinging a URL — the job can do real work, talk to your database, and exit.
# Standard cron syntax
0 2 * * * # every day at 02:00 — e.g. nightly cleanup
*/15 * * * * # every 15 minutes — e.g. sync jobThe nice part is co-location: a cronjob can share the same managed database as your app (with DATABASE_URL injected), so scheduled maintenance, report generation, or data syncs run right next to the data. This is ideal for the Laravel scheduler (schedule:run), Rails rake tasks, Django management commands, or any "run my container on a timer" job.
Honest limits: PandaStack cronjobs are compute-cron (they run your code), not a standalone uptime-monitoring product — for dead-man's-switch alerting on jobs you'd still pair with a monitor like Healthchecks.io. It's a newer platform with a growing ecosystem.
Designing schedulable jobs that don't bite you
- Make jobs idempotent. Assume a job might run twice; design so a double-run is harmless.
- Guard against overlap. If a run can exceed the interval, use a lock (DB advisory lock, Redis lock) or a concurrency policy.
- Set timeouts. A hung job should fail loudly, not run forever.
- Emit a heartbeat to a monitor so you're alerted on *non-execution*, not just failures.
- Log structured output per run so you can debug a specific execution.
References
- [Crontab expression reference (crontab.guru)](https://crontab.guru/)
- [Kubernetes CronJob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)
- [Google Cloud Scheduler](https://cloud.google.com/scheduler/docs)
- [GitHub Actions scheduled events](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule)
- [Healthchecks.io (cron monitoring)](https://healthchecks.io/docs/)
---
Need scheduled jobs that run real code next to your database with live per-run logs? PandaStack's free tier includes cronjobs alongside your apps and a managed DB. Set one up at [dashboard.pandastack.io](https://dashboard.pandastack.io).