Python Scripts with cron and systemd Timers
"It works when I run it and not from cron" is almost always the environment. This guide runs one script three ways and counts what it gets: 16 environment variables interactively, 6 under cron, 22 under systemd - and shows cron using your home directory as the working directory while systemd honours WorkingDirectory=. Then the part cron cannot do: a recorded failure you can query afterwards.
OS and Process Automation Guide 23 of 39 Intermediate
- Control nodeUbuntu 26.04 LTS
- Python3.14.4
- requests2.34.2
- paramiko5.0.0
- TimeAbout 20 min
systemd user timers need a logged-in session or loginctl enable-linger. OnUnitActiveSec and Persistent= are long-standing options; systemd-analyze verify will check a unit before you install it.
- pytest9.1.1
- PyYAML6.0.3
- boto3 / botocore1.43.78
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| RUNNER01 | 192.168.0.27 | Ubuntu 26.04 LTS | Control node - every script in this path runs here | 2 Core | 4 GB | 50 GB |
This guide includes
Use this when it works by hand and not from cron. This matters because the difference is almost always the environment the script was handed - and one script run three ways shows exactly what each caller gives it.
- running one script three ways, and comparing what each gave it
- writing a crontab entry in full, and seeing what cron provided
- writing the same job as a systemd timer, with a service and a timer unit
- reading what the journal recorded, including a failing run
- putting all three runs side by side in one file
Before you start
- guide 9 - the same problem from the script's side.
- guide 22 - which a scheduled job needs.
- guide 8 - the interpreter path both use.
-
One script, run three ways
-
A crontab entry, in full
-
What cron gave it
-
The same job as a systemd timer
-
Start it, and what systemd gave it
-
What the journal recorded
-
A failing run, and what systemd says about it
-
The whole comparison, in one file