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>