DEV Community

Joscha Schultze for Cronjob.de

Posted on AI-assisted

How to run cron jobs without a server: 4 ways and when to use which

Sooner or later almost every web project needs something to happen on a schedule: send the newsletter at 8, clean up old sessions every night, import a price feed every 15 minutes, run wp-cron.php reliably.

On a VPS you open crontab -e and you're done. But many projects don't have that: shared hosting without SSH, a serverless app, a static site with an API route, a WordPress or Shopware shop where the host doesn't offer cron.

I've been running a cron service for many years, so I see these setups every day. The most common question I get is still: “It works when I open the URL in my browser, so why does it fail on schedule?” Usually it’s a timeout, a login wall or a firewall blocking unknown IPs. Here are the four approaches that actually work, and where each one stops working.

The one question that decides it

Can your job be triggered by an HTTP request?

  • Yes. It's a PHP script, an API route, a webhook, wp-cron.php. Then you only need something that calls a URL on a schedule. That's options 1 to 3.
  • No. It's a Python script, a binary, a data pipeline that needs its own runtime. Then you need a platform that runs your code. That's option 4.

Most "I need a cron job" questions on shared hosting are the first kind, even if they don't look like it at first.

1. An online cron service (URL calls on a schedule)

You enter a URL and a schedule, the service calls it, logs the response and tells you when it fails.

Typical setup for a PHP job on shared hosting:

<?php
// cron/cleanup.php: protect it with a secret, it's a public URL
if (($_GET['key'] ?? '') !== getenv('CRON_KEY')) {
    http_response_code(403);
    exit;
}
// ... do the work ...
echo "deleted 42 sessions";
Enter fullscreen mode Exit fullscreen mode

Then schedule https://example.com/cron/cleanup.php?key=… every hour.

Services: cron-job.org (free, run from Germany), EasyCron, Cronjob.de (that's mine: free tier and paid plans, servers in the EU).

Good for: shared hosting, WordPress (wp-cron.php), Laravel's schedule:run behind a route, any API endpoint.

Watch out for:

  • Timeouts. The service waits a limited time for your response (often 30 to 60 seconds). Long jobs should return quickly and keep working in the background, or be split into batches.
  • Security. The URL is public. Use a secret key or basic auth, and don't put real credentials in the query string if you can avoid it.
  • "200 OK" isn't "it worked". If your script catches an error and still prints a page, the call looks successful. Return a non-2xx status code on failure, or check the response body for an expected text. Some services can do that for you.

2. GitHub Actions on a schedule

If your code is on GitHub anyway, a workflow can run on a cron schedule:

# .github/workflows/nightly.yml
on:
  schedule:
    - cron: "17 3 * * *"   # 03:17 every day
      timezone: "Europe/Berlin"   # default is UTC
  workflow_dispatch:       # allows manual and external triggers

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: python scripts/nightly.py
Enter fullscreen mode Exit fullscreen mode

Good for: jobs that run your repository's code, a few times per day.

Watch out for:

  • It's not precise. GitHub says scheduled runs "can be delayed during periods of high loads", and under enough load queued jobs may be dropped. The shortest interval is 5 minutes.
  • Scheduled workflows are disabled in public repositories after 60 days without repository activity.
  • The default time zone is UTC. Add a timezone to the schedule (as above) if the job should follow local time.

If timing matters, a common trick is to keep the workflow_dispatch trigger and start the workflow from outside through the GitHub API on a reliable schedule.

3. Your cloud provider's scheduler

AWS EventBridge Scheduler, Google Cloud Scheduler and Azure Logic Apps all call an HTTP endpoint or a function on a cron schedule.

Good for: projects already running on that cloud, with IAM, retries and logging in one place.

Watch out for: setup effort (accounts, roles, billing) that is out of proportion if all you need is "call this URL every 10 minutes". Pricing is usually small but not always zero.

4. A platform that runs your code

Render, Railway, Fly.io, Kuberns and similar platforms have "cron job" services: you point them at a repository, give them a command and a schedule, and they start a container for each run.

Good for: jobs that are not an HTTP endpoint: Python scripts, workers, anything that needs its own runtime and more time.

Watch out for: cold starts, per-run costs, and that the job runs in a separate environment from your app, so it needs its own access to your database.

The part everyone forgets: knowing when a job didn't run

All four options answer "how do I start the job?". None of them, by default, answers "how do I notice when it silently stopped?"

The pattern for that is a heartbeat (also called a dead man's switch): at the end of each successful run, the job pings a URL. If the ping doesn't arrive in time, you get an alert.

# crontab on your own server
0 * * * * php import.php && curl -fsS https://runcheck.de/abc123
Enter fullscreen mode Exit fullscreen mode

The && matters: the ping only goes out if the job exited successfully. This works for jobs on your own server, in GitHub Actions, n8n or anywhere else that can make an HTTP request.

Dedicated tools for this are Healthchecks.io, Cronitor and Dead Man's Snitch. Some cron services (including mine) combine both: they call your URLs and watch the heartbeats of jobs that run elsewhere, so everything scheduled is in one list.

Quick decision table

Your situation Use
Shared hosting, PHP, WordPress, an API route Online cron service (1)
Code lives on GitHub, timing can be loose GitHub Actions (2)
Already deep in AWS/GCP/Azure Cloud scheduler (3)
Not an HTTP endpoint, needs its own runtime Code-running platform (4)
Jobs already run somewhere, but you don't trust them Add heartbeat monitoring

Disclosure: I run Cronjob.de, one of the services mentioned in option 1. I've tried to describe all options as they are. If I got something wrong about another tool, tell me in the comments and I'll fix it.

Top comments (0)