A script that runs fine on my laptop is one thing. A script that runs at 4 AM, on a server, with nobody watching, is a different animal — and my first attempt at moving one over was a small comedy of errors. It now runs every night without me. The difference between then and now is that I stopped improvising and wrote a checklist. Here it is, followed by the five mistakes that made the checklist necessary.
1. Copy the script up. scp is "copy a file over SSH", nothing more:
scp tidy_downloads.py me@203.0.113.7:/home/me/scripts/
The path after the colon is where the file lands on the server.
2. Give it a venv on the server. Same idea as on your own machine — its own folder of packages, so the server's system Python stays untouched:
python3 -m venv /home/me/venvs/tidy
/home/me/venvs/tidy/bin/pip install requests
3. Make every path absolute. Cron does not start your script where you think it does (mistake three, below). My script builds its paths from Path(__file__).parent and from /home/me/..., never from wherever it happens to be running.
4. Schedule it with cron. Run crontab -e and add:
30 4 * * * /home/me/venvs/tidy/bin/python /home/me/scripts/tidy_downloads.py >> /home/me/logs/tidy.log 2>&1
Five fields: minute, hour, day of month, month, weekday — so this is 4:30 every morning. Two details that matter. The interpreter is the venv's python, not bare python. And the two redirects at the end append both output and errors to a log file; without them, cron mails everything to a local mailbox that nobody has opened since 1998.
5. Log from inside the script too. When it misbehaves at 4 AM, the log is the only witness:
import logging
logging.basicConfig(
filename="/home/me/logs/tidy.log",
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
logging.info("started")
One trap: logging creates the log file but not the log folder. If /home/me/logs doesn't exist, the script dies on startup with a FileNotFoundError — which you will only notice promptly because of step 4's redirect.
python instead of python3. My Ubuntu server has no plain python command. python: command not found — and for ten confused minutes I assumed the copy had failed.error: externally-managed-environment. Step 2 exists because of this mistake.Path("reports") quietly resolved somewhere I never intended — and the first run "succeeded" while moving nothing at all.>> log 2>&1 ever since.match statement is a SyntaxError. Now I run python3 --version before writing anything clever.The grown-up answer to all of this is a systemd timer, and one day I'll sit down and learn it. But cron at 4:30 has gotten up every morning for two years without asking me a single question, and at my scale that kind of reliability is worth more than modernity.