Render's Cron Jobs and Railway's cron feature both do the same thing: spin up an ephemeral container on a schedule, make sure the last run finished before starting the next one, and bill you for however many seconds it was alive. Kubernetes has shipped that exact primitive — CronJob, built on top of Job — as a free, built-in API object since v1.21, half a decade ago. It costs nothing beyond the node it runs on.
So what does the hosted version of a five-year-old free primitive actually cost a team running a normal number of scheduled tasks? Below is the line-by-line math at 1, 5, 20, and 100 jobs a month, and the crossover point where "billed per second" stops being the cheap option. Spoiler: it isn't a huge, compute-hungry job that breaks the hosted pricing model — it's the ordinary habit of accumulating a lot of small ones.
The same primitive, three implementations
Strip away the marketing and all three products are doing one thing: run a container when the clock says to, and don't let two copies of it run at once. The difference is what happens when a run is still going when the next tick arrives.
| Overlap handling | Minimum interval | Max run length | |
|---|---|---|---|
| Render Cron Jobs | Delays the next scheduled run until the active one finishes (does not skip it) | None documented | 12 hours, then force-stopped |
| Railway cron | Skips the new run if the previous one hasn't finished | 5 minutes | None documented, but the platform expects services to exit promptly |
Kubernetes CronJob | Your choice: Allow (run concurrently), Forbid (skip, same as Railway), or Replace (kill and restart, closest to Render's queue-and-run) | Whatever your cron syntax can express (practically 1 minute) | Unbounded, or set your own via activeDeadlineSeconds |
Kubernetes also has startingDeadlineSeconds, which controls how long a missed run is allowed to start late before it's abandoned entirely — a knob neither hosted platform exposes to you directly. None of this is exotic; it's the same scheduling logic every cron-like system needs.
The overlap-handling column isn't just trivia — it changes what "reliable" means on each platform. Render's queue-and-run means a job that occasionally runs long doesn't lose executions; it just pushes them later, which is safe for things like report generation but dangerous for things like "poll an external API every 5 minutes," where a pileup of delayed runs can eventually fire in a burst. Railway's skip-on-overlap (identical to Kubernetes Forbid) is the opposite trade: no burst risk, but a job that occasionally runs long silently loses executions, so anything that must run exactly once per period needs its own idempotency or alerting.
Railway's 5-minute floor on scheduling frequency is also a hard ceiling Kubernetes doesn't share — a CronJob can fire once a minute if the workload calls for it. The only place the three products actually diverge in the ways that show up on an invoice is the bill.
What N scheduled jobs a month actually costs
Take a realistic workload: a small task — a cache warm, a report export, a webhook retry sweep — running once a day, taking about 2 minutes, on roughly 0.5 vCPU / 512 MB. That's the Render "Starter" instance size and a proportionate Railway container. Here's what happens as the number of distinct scheduled jobs (not runs) grows.
Render Cron Jobs bills each cron job at the same per-second rate as a web service (Starter instance list price: $7/month, ≈ $0.0096/hour), but with a $1/month minimum per cron job service — a floor that applies per job, not per run. A job like the one above runs for 1 hour of total compute a month (30 runs × 2 minutes), which prices out to roughly $0.01 in actual usage. The floor swallows that immediately: every job on Render costs at least $1/month regardless of how little it runs.
Railway cron meters CPU and RAM separately and pools the result against your plan's included credit: $20/vCPU-month ($0.000463/vCPU-minute) and $10/GB-month ($0.000231/GB-minute), billed per second. The same job (0.5 vCPU, 0.5 GB, 2 minutes × 30 runs = 60 minutes/month) costs:
60 min × (0.5 × $0.000463 + 0.5 × $0.000231) ≈ $0.021/month in raw usageThat's billed against the Hobby plan's $5/month included credit — so unless your aggregate cron usage across every job exceeds $5, the marginal cost of adding another job is close to zero. The floor isn't per-job; it's per-account.
Kubernetes CronJob on a self-hosted fleet has no per-job billing surface at all. A single Hetzner CX22 (2 vCPU / 4 GB, ~$4.59/month as of the June 2026 repricing) has enough headroom to run dozens of jobs like this one — they're brief and staggered, not concurrent — for the price of the box you're already running other things on.
| Jobs/month | Render (per-job $1 floor) | Railway (pooled usage vs. $5 Hobby credit) | Self-hosted CronJob on one Hetzner CX22 |
|---|---|---|---|
| 1 | $1.00 | $5.00 (plan floor) | $4.59 (box you already run) |
| 5 | $5.00 | $5.00 (plan floor) | $4.59 |
| 20 | $20.00 | $5.00 (usage ≈ $0.42, still under credit) | $4.59 |
| 100 | $100.00 | $5.00 (usage ≈ $2.10, still under credit) | $4.59 (or one size up if CPU headroom gets tight) |
Below 5 jobs, Render is actually the cheapest of the three — a $1 floor beats a $5 plan minimum. Past that, the lines diverge hard: Render climbs $1 for every job you add, forever, because the floor is job-scoped. Railway and a self-hosted node both stay flat, because their floors are account-scoped and infrastructure-scoped respectively, not job-scoped. At 100 lightweight scheduled jobs, Render costs 20-24x what the other two cost.
Does a heavier job change the picture? Swap the assumption: instead of 2 minutes/day at 0.5 vCPU, make it an hour a day at 1 vCPU / 2 GB — a real nightly ETL step, not a health check. That's 30 hours/month of compute:
- Render, on a Standard instance ($25/month, ≈ $0.0343/hour), bills 30 hours × $0.0343 ≈ $1.03/month — usage-priced now, just barely past the $1 floor.
- Railway bills 1,800 minutes × (1 × $0.000463 + 2 × $0.000231) ≈ $1.67/month — still comfortably inside the $5 Hobby credit.
- Self-hosted
CronJobstays at the same $4.59/month box, assuming the hour-long run fits the node's spare CPU/RAM window.
One heavy job barely moves any of the three numbers — a single job, however chunky, rarely crosses either hosted platform's floor by much. It's twenty single-minute jobs that costs $20/month on Render, not one twenty-minute job. The floor punishes job count, not job size, which is exactly backward from how most teams budget for compute.
Why the floors behave so differently
The mechanism is worth naming explicitly, because it's the whole story: Render's minimum charge is attached to the job definition; Railway's is attached to the account. Adding a 21st cron job on Render always adds another dollar, no matter how trivial the job is — a health-check ping, a stale-session sweep, a "did the backup finish" alert all cost the same $1 floor as a job that actually needs a Starter instance's worth of compute. Railway doesn't care how many job definitions you have; it cares how much CPU-time and memory-time they collectively burn, and light jobs simply don't move that number.
A self-hosted CronJob inherits neither constraint, because there's no billing layer between the job and the node at all — the Kubernetes control plane schedules Job pods the same way it schedules anything else, and the only thing that costs money is the node capacity you provisioned regardless of cron. Teams that accumulate a lot of small scheduled tasks — and most platform teams do, because "just add a cron job" is the path of least resistance for one-off maintenance scripts — are the ones who feel Render's per-job floor first. It's not a pricing edge case; it's the default shape of how cron jobs accumulate.
The honest caveat: self-hosted isn't literally free
Zero billing surface is not the same as zero cost. A Hetzner CX22 has 2 vCPU and 4 GB of RAM total — that's a hard ceiling, not an accounting abstraction. Pack enough concurrent or heavier jobs onto it and you'll need a bigger box or a second node, which is a real dollar increase, just a much coarser and less frequent one than "another dollar per job."
And somebody still has to keep the cluster patched, the node pool sized, and the CronJob manifests correct — that operational cost is real even when the invoice line for it is $0. What self-hosting actually buys you here isn't "free," it's "the marginal cost of one more job is zero until you hit a real capacity wall," instead of "the marginal cost of one more job is $1, always, no matter how small."
There's also a reliability trade worth stating plainly: Render and Railway run their control planes, so a scheduled job firing on time is someone else's uptime problem. A self-hosted CronJob makes that your problem — if the cluster's control plane or the kube-controller-manager has an outage window, scheduled runs during that window are missed the same way any other cluster-dependent workload would be. For a team already operating a Cluster API fleet for its regular services, that's not new risk, just the same risk cron jobs now share with everything else running there. For a team with no fleet today, "self-host CronJob to save $15/month" is not, by itself, a reason to stand one up.
Where this fleet actually runs
For a team already running a Cluster API fleet — or thinking about moving scheduled tasks off a hosted PaaS onto owned infrastructure — the CronJob object doesn't need a special product tier or a "cron jobs" line item on a pricing page. It's a standard Kubernetes resource that ships with the cluster.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with the full Kubernetes API (including CronJob) underneath rather than a metered wrapper around it. Star the repo on GitHub or deploy your first app today.
Sources: