I Found Out GitHub Actions Runs Cron Jobs for Free (and It Solved a Problem I Didn’t Know Had a Solution)
How to use the GitHub Actions scheduler as a free, reliable cron job, after discovering Vercel’s native cron simply wasn’t firing on the free plan.
I was setting up daily reminders for Haven — a push notification that fires at a specific time, every day. Vercel has a built-in feature for this, "Cron Jobs": you register an API route and a schedule, and it calls that route on its own, every day. I set it up, tested it, and it seemed right.
Except, checking the logs a few days later, I noticed the route had simply never been called. Zero real executions in over three days, even with the cron registered correctly. I looked into it and found out this isn’t unusual on Vercel’s free plan — Cron Job reliability can vary, and you can’t blindly depend on it for something that needs to fire every single day.
The solution I didn’t expect: GitHub Actions
I already used GitHub Actions for other things — running tests automatically on every push, for example. What I didn’t know is that it also has a scheduling trigger ("schedule"), just like a real server cron, and it runs for free in GitHub repositories.
The idea is simple: instead of Vercel calling my own route, I create a GitHub Actions workflow that, at a set time, makes an HTTP request to that route. The route doesn’t even need to know the difference — it’s just waiting to be called with the right credential, no matter where the call comes from.
What it looks like in practice
- A file inside .github/workflows/ in your repository, for example send-reminders.yml.
- A "schedule" trigger with a cron expression (the usual format: minute, hour, day of month, month, day of week) — for example "0 9 * * *" runs every day at 9am UTC.
- A second "workflow_dispatch" trigger, which adds a manual "Run workflow" button in the Actions tab — great for testing without waiting for the scheduled time.
- A step that just makes an authenticated curl request to your own API route, passing a secret (stored in the repository’s GitHub Secrets) as an authorization token.
The API route itself is still responsible for validating that secret before doing anything — GitHub Actions is only saying "it’s time," and it’s still your own backend that decides whether the call is legitimate, exactly as it was before.
Why this surprised me
I always thought of GitHub Actions as a CI/CD tool — run tests, deploy, check lint. It hadn’t occurred to me that the same scheduler that fires those workflows on every push can also be configured to fire on its own, at a fixed time, with no push happening at all. And the best part: within GitHub’s free-tier limits, this comfortably covers one call a day — it’s a run that takes a few seconds, not a server running around the clock.
It’s not a solution exclusive to push reminders. Anything that needs to "happen on its own, every day, without anyone clicking anything" — clearing out old data, sending a daily e-mail summary, syncing something with an external service — can use this exact same pattern, with no extra infrastructure and no paid cron provider.