Cron Scheduling Guide: Automating Recurring Tasks

🌐 한국어

Start the bot at 8:50 every morning, produce a report after the close, tidy the logs overnight. Do that by hand and you will eventually forget. cron is Linux's built-in scheduler for "run this command at this time." Thirty-plus years of use have made it simple and dependable.

That said, it almost never works the first time you use it. Why that happens is the heart of this article.

The Five Fields

A cron schedule is five space-separated columns.

┌───── minute       (0-59)

│ ┌─── hour         (0-23)

│ │ ┌─ day of month (1-31)

│ │ │ ┌───── month       (1-12)

│ │ │ │ ┌─── day of week (0-7, 0 and 7 are Sunday)

│ │ │ │ │

* * * * *  command to run

You only need four symbols.

  • * — every value
  • , — a list. 0,30 = minute 0 and minute 30
  • - — a range. 1-5 = Monday through Friday
  • / — a step. */10 = every 10 minutes

Common examples:

0 9 * * 1-5      9:00 AM on weekdays

50 8 * * 1-5     8:50 AM on weekdays (10 minutes before the open)

*/5 * * * *      every 5 minutes

0 */2 * * *      every 2 hours, on the hour

30 3 * * 0       Sunday at 3:30 AM

0 0 1 * *        midnight on the 1st of every month

One easily confused point — specifying both day-of-month and day-of-week makes them an OR. 0 9 1 * 1 runs "if it's the 1st or it's a Monday." Not AND.

Register entries by opening the editor with crontab -e and writing one per line. Check them with crontab -l.

The Four Traps Every Beginner Hits

"I added it to cron and nothing happens" is almost always one of these four.

① There's No PATH

Cron does not inherit your shell environment. It doesn't read .bashrc, and PATH is nearly empty. The python that worked in your terminal becomes "command not found" under cron.

The fix is absolute paths everywhere.

# Bad

0 9 * * 1-5 python bot.py

# Good — absolute path for both the interpreter and the script

0 9 * * 1-5 /opt/bot/.venv/bin/python /opt/bot/bot.py

If you use a virtual environment, don't go through activatepoint directly at the Python inside the venv. That's the most reliable form.

② The Working Directory Is Different

Cron typically runs commands from your home directory. Use a relative path in your code, like open("config.ini"), and the file won't be found.

# Change directory before running

0 9 * * 1-5 cd /opt/bot && /opt/bot/.venv/bin/python bot.py

The better approach is anchoring the base path in the code itself.

from pathlib import Path

BASE = Path(__file__).resolve().parent   # relative to the script's own location

config_path = BASE / "config.ini"

③ The Output Goes Nowhere

A cron job's output is mailed by default, and with no mail configured it simply vanishes. Failures leave no trace and you can't find the cause. Always write to a log.

# Append both stdout and stderr to a file

0 9 * * 1-5 cd /opt/bot && ./run.sh >> /var/log/bot/cron.log 2>&1

2>&1 is what sends error output to the same file. Without it, the very error message you need disappears.

④ Timezone

If the server runs UTC, so does cron. 0 9 * * * fires at 6 PM Korean time. Check the server clock with date first, and declare it at the top of the crontab if needed.

CRON_TZ=Asia/Seoul

0 9 * * 1-5 /opt/bot/.venv/bin/python /opt/bot/bot.py

Preventing Overlap — flock

What happens when a job scheduled every 5 minutes takes 6? Cron doesn't check whether the previous run finished. Processes pile up until the server falls over. If the script places orders, you get duplicate orders.

flock solves it in one line.

# -n : if the lock can't be acquired, exit immediately rather than wait

*/5 * * * * /usr/bin/flock -n /tmp/report.lock /opt/bot/report.sh >> /var/log/bot/report.log 2>&1

If the previous run is still going, this round is quietly skipped. Put it on essentially every recurring cron job.

On Windows — Task Scheduler

Windows has no cron; Task Scheduler fills the role. The GUI works, but registering by command is reproducible, which is better.

REM Start the bot at 8:50 AM on weekdays

schtasks /create /tn "TradingBotStart" /tr "C:\bot\run.bat" /sc weekly /d MON,TUE,WED,THU,FRI /st 08:50

REM Verify registration / run it immediately as a test

schtasks /query /tn "TradingBotStart"

schtasks /run   /tn "TradingBotStart"

Two places people commonly get stuck on Windows. If "Run only when user is logged on" is enabled, nothing runs after a reboot until someone logs in — switch it to "Run whether user is logged on or not." And if you don't set "Start in", relative paths break in exactly the same way as cron's trap ②.

A Real Combination — One Day's Schedule

In practice you bundle it like this.

CRON_TZ=Asia/Seoul

# 08:50 start the bot before the open (weekdays)

50 8 * * 1-5 /usr/bin/flock -n /tmp/bot.lock /opt/bot/start.sh >> /var/log/bot/start.log 2>&1

# 15:40 stop the bot after the close + send the daily report to Telegram

40 15 * * 1-5 /opt/bot/stop_and_report.sh >> /var/log/bot/report.log 2>&1

# every 30 minutes, liveness check — alert if it's dead

*/30 * * * * /usr/bin/flock -n /tmp/hc.lock /opt/bot/healthcheck.sh >> /var/log/bot/hc.log 2>&1

# daily at 3 AM, delete logs older than 30 days

0 3 * * * find /var/log/bot -name "*.log" -mtime +30 -delete

Don't skip that last line. Without log cleanup, the disk fills a few months later and kills the bot. It's a genuinely common incident.

When Cron Isn't the Right Tool

Cron's smallest unit is the minute, and every run spawns a fresh process. So don't use it for these.

  • Second-scale repetition — handle it with a loop or timer inside the bot
  • Work that must retain state — a new process each time means in-memory state is gone
  • Restart immediately on crash — that's the domain of systemd or Docker's restart policy, not cron

Draw the line like this: "a job done once at a set time" is cron; "a job that must keep running" is a process manager.

Summary

  • The expression is five fields — minute, hour, day-of-month, month, day-of-week — and day-of-month with day-of-week is an OR
  • Cron doesn't inherit your shell environment → absolute paths for everything
  • The working directory differs → cd first, or anchor paths on __file__ in code
  • Output vanishes → >> logfile 2>&1 is mandatory
  • Check the server timezone; declare CRON_TZ if needed
  • Block overlapping runs on recurring jobs with flock -n
  • On Windows use schtasks, watching "run whether logged on or not" and "Start in"
  • Always include a log-cleanup job — the disk is what kills the bot

Cron takes 20 minutes to learn and 2 hours to get trapped by. Know these four traps going in and you skip those 2 hours.

댓글

이 블로그의 인기 게시물

한국투자증권 KIS API로 실시간 시세 받기 (WebSocket 실전)

파이썬으로 업비트 API 연동하기 — 시세 조회부터 주문까지 기초

Go로 자동매매 신호봇 프레임워크 설계하기