Troubleshooting · Hetzner

Hetzner cron job not running? Here's how to fix it

A practical checklist for cron jobs that won't run on a Hetzner Cloud server or dedicated box — environment, paths, timezone, and mail.

If a cron job works when you run it by hand but never fires from crontab on your Hetzner server, it’s almost always one of a handful of causes. Work down this list.

1. Confirm cron is actually running

On a fresh Hetzner Cloud image, the cron daemon should be active — but verify:

systemctl status cron      # Debian/Ubuntu
systemctl status crond     # CentOS/Rocky/Alma

If it’s inactive, enable and start it:

sudo systemctl enable --now cron

2. Check the job is in the right crontab

A job in your user crontab (crontab -e) runs as you; a job in /etc/crontab or /etc/cron.d/* must include a user field. Editing the wrong one is the most common mistake:

crontab -l            # your user's jobs
sudo crontab -l       # root's jobs
cat /etc/cron.d/*     # system jobs (need a user column)

3. The PATH is minimal under cron

Cron runs with a bare environment — typically PATH=/usr/bin:/bin. A script that calls node, python3, docker, or pg_dump will fail with “command not found” even though it works in your shell. Use absolute paths, or set PATH at the top of the crontab:

PATH=/usr/local/bin:/usr/bin:/bin
0 2 * * * /usr/local/bin/node /opt/app/job.js

4. Environment variables are missing

Cron does not load ~/.bashrc, ~/.profile, or /etc/environment the way an interactive login does. Secrets and config you rely on simply aren’t there. Source them explicitly:

0 2 * * * . /opt/app/.env && /opt/app/run.sh

5. Check the server timezone

Hetzner images often default to UTC. 0 9 * * * then fires at 09:00 UTC, not your local 09:00. Check and, if needed, set it:

timedatectl                       # see current timezone
sudo timedatectl set-timezone Europe/Berlin

6. Read the logs

Cron logs every fire. If you don’t see your job, it isn’t matching:

grep CRON /var/log/syslog         # Debian/Ubuntu
journalctl -u cron --since today

No log line at all usually means a bad schedule expression or the wrong crontab.

The deeper problem: you only noticed because you looked

Every fix above assumes you went looking. The job that silently stopped six weeks ago is the one that hurts. Cron has no concept of success or failure and won’t tell you when it stops.

Add a heartbeat: have the job ping a URL when it finishes, and get alerted the moment a ping is missed. One line at the end of your script:

curl -fsS https://ping.steadycron.com/<your-ping-token>