DEV Community

Niuniu Ox
Niuniu Ox

Posted on

I Moved My Cron Jobs Off a $10/Month VPS to Cloudflare Workers + Cron Triggers — Bill: $0

I was paying DigitalOcean $10/month for a droplet whose entire job was running six cron scripts: a nightly database backup, an RSS digest, a sitemap ping, a quota checker, a weekly report, and a cleanup task. Total CPU time per day: under 4 minutes. I was renting a whole server to be awake 0.3% of the day.

So I moved all six to Cloudflare Workers with Cron Triggers. The bill is now $0. Here's what actually happened, with numbers.

The migration, by the numbers

Job Old (droplet cron) New (Workers Cron) Cold start p50
Nightly DB backup (Neon → R2) 41s 38s 6ms
RSS digest (12 feeds) 9s 11s 4ms
Sitemap ping 1.2s 0.9s 3ms
Quota checker (3 APIs) 6s 7s 5ms
Weekly report 22s 24s 7ms
Cleanup task 15s 13s 4ms

The Workers free tier gives you 100,000 requests/day and Cron Triggers on the free plan. My six jobs use ~180 invocations/day. I'm at 0.18% of the free quota.

Setup per job is genuinely small:

export default {
  async scheduled(event, env, ctx) {
    const res = await fetch("https://api.example.com/backup", {
      method: "POST",
      headers: { "Authorization": `Bearer ${env.BACKUP_TOKEN}` },
    });
    console.log("backup:", res.status);
  },
};
Enter fullscreen mode Exit fullscreen mode
# wrangler.toml
[triggers]
crons = ["0 3 * * *"]
Enter fullscreen mode Exit fullscreen mode

npx wrangler deploy and it's scheduled. No systemd, no crontab -e, no server to patch.

The honest caveat

Workers has a 30-second wall-clock limit on free-tier scheduled handlers (CPU time limit is separate and lower). My backup job at 38s only passed because most of that time is waiting on fetch — wall-clock waiting doesn't count against CPU time on Workers, and Cron Triggers actually allow up to 15 minutes of wall time on the free plan. But if your job is 10 minutes of pure CPU (compressing a huge archive locally, say), this doesn't fit. One of my seven candidate jobs stayed behind on a $4 Hetzner box for exactly that reason.

Also: no persistent filesystem. Anything stateful goes to R2/KV/D1, which is fine for me but is a real design constraint, not a footnote.

The controversial part

If your server exists to run cron jobs and serve a small API, renting a VPS in 2026 is a nostalgia habit, not an architecture decision. The free tiers of Cloudflare + Neon + Upstash cover what a $10 droplet did for most side projects — and you stop being on-call for apt security updates forever.

I drafted the migration checklist with an AI pair programmer (MonkeyCode, free: https://ly.cyberserval.tech/iIETXiF) — it caught that my sitemap ping was using a 30s timeout that would blow past nothing on Workers but would've masked a hanging fetch.

Are you still running a VPS just for scheduled jobs, or have you moved your crons to serverless? What's the job that won't fit?

Top comments (0)