schedule และ clock automation

Cron Jobs บน VPS ตั้งค่าให้ถูกต้อง ไม่ให้พลาดและไม่ให้ซ้ำ

มิถุนายน 2026 · อ่าน 6 นาที

Cron เป็น task scheduler มาตรฐานบน Linux ใช้สำหรับรัน script อัตโนมัติตามเวลา แต่มีรายละเอียดที่ทำให้หลายคนตั้งค่าผิดและสงสัยว่าทำไม cron ถึงไม่รัน


Crontab Syntax

# ┌── นาที (0-59)
# │ ┌── ชั่วโมง (0-23)
# │ │ ┌── วันในเดือน (1-31)
# │ │ │ ┌── เดือน (1-12)
# │ │ │ │ ┌── วันในสัปดาห์ (0-7, ทั้ง 0 และ 7 คือ อาทิตย์)
# │ │ │ │ │
# * * * * * command
ตัวอย่างความหมาย
* * * * *ทุกนาที
0 * * * *ทุกชั่วโมง (นาทีที่ 0)
0 2 * * *ทุกวัน ตี 2
0 2 * * 0ทุกวันอาทิตย์ ตี 2
0 2 1 * *วันที่ 1 ของทุกเดือน ตี 2
*/15 * * * *ทุก 15 นาที
0 9,17 * * 1-59 โมงและ 5 โมงเย็น วันจันทร์–ศุกร์

แก้ไข Crontab

crontab -e    # แก้ไข crontab ของ user ปัจจุบัน
crontab -l    # ดู crontab ที่มีอยู่
crontab -r    # ลบ crontab ทั้งหมด (ระวัง!)

# แก้ไข crontab ของ user อื่น (ต้องเป็น root)
crontab -u www-data -e

ปัญหาที่พบบ่อยที่สุด: PATH

Cron รัน script ด้วย environment ที่จำกัดมาก PATH ของ cron ไม่เหมือนกับที่คุณพิมพ์ใน terminal คำสั่งหลายตัวอาจหาไม่เจอ

วิธีแก้: ใช้ absolute path เสมอ

# ผิด
0 2 * * * backup.sh

# ถูก
0 2 * * * /usr/local/bin/backup.sh

# หรือกำหนด PATH ไว้บน crontab
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 2 * * * backup.sh

Logging: รู้ว่า Cron รันจริงไหม

ค่า default cron ส่ง output ทาง email (ซึ่งมักไม่มีใคร set) ให้ redirect ไป log file แทน:

0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

2>&1 คือส่ง stderr ไปรวมกับ stdout ใน log file เดียวกัน

ป้องกัน Cron รันซ้อนกัน (Overlapping)

ถ้า cron รันทุก 5 นาทีแต่ script ใช้เวลา 10 นาที รอบถัดไปจะเริ่มก่อนรอบแรกเสร็จ ใช้ flock ป้องกัน:

*/5 * * * * flock -n /tmp/myjob.lock /usr/local/bin/myjob.sh

-n คือ non-blocking ถ้า lock ไม่ได้ก็ข้ามไปเลย ไม่รอ

ใช้ /etc/cron.d/ สำหรับ System-wide Jobs

แทนที่จะใส่ cron ใน crontab ของ user สามารถสร้างไฟล์ใน /etc/cron.d/ ได้ ต้องระบุ user ด้วย:

# /etc/cron.d/myapp
0 2 * * * www-data /var/www/myapp/scripts/cleanup.sh >> /var/log/myapp-cleanup.log 2>&1

Systemd Timers: ทางเลือกแทน Cron

Systemd timers มีข้อดีเหนือกว่า cron ในบางด้าน:

# สร้าง service file: /etc/systemd/system/mybackup.service
[Unit]
Description=Daily Database Backup

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/backup.sh

# สร้าง timer file: /etc/systemd/system/mybackup.timer
[Unit]
Description=Run Daily Backup at 2am

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true   # รันทันทีถ้า miss เวลาไปเพราะ server ดับ

[Install]
WantedBy=timers.target
systemctl enable --now mybackup.timer
systemctl list-timers   # ดู timer ทั้งหมด
journalctl -u mybackup.service   # ดู log

ทดสอบ Cron Environment ก่อน Deploy

วิธีง่ายที่สุดคือ simulate cron environment ด้วย:

# รัน script ด้วย environment เหมือน cron
env -i HOME=/root PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin \
  bash /usr/local/bin/backup.sh

# ถ้า script รันได้ cron จะรันได้เช่นกัน

ตรวจสอบว่า Cron Job ทำงานถูกต้อง

หลังตั้ง cron แล้ว ยืนยันว่า daemon ทำงานและ job ถูกรันจริงด้วยคำสั่งเหล่านี้:

# 1. cron daemon ทำงานอยู่หรือไม่
systemctl status cron        # Debian/Ubuntu
systemctl status crond       # CentOS/RHEL

# 2. ดู crontab ของ user ปัจจุบัน
crontab -l

# 3. ตรวจ log ว่า cron ถูก trigger จริง
grep CRON /var/log/syslog | tail -20          # Debian/Ubuntu
journalctl -u cron --since "10 min ago"       # systemd

# 4. ทดสอบ job ที่เขียน output ไฟล์
echo '* * * * * date >> /tmp/cron-test.log' | crontab -
sleep 70 && cat /tmp/cron-test.log   # ต้องเห็นเวลาเพิ่มขึ้นทุกนาที
Tip: ถ้า grep CRON เห็นว่า cron ถูกเรียกแต่ job ไม่ทำงาน มักเป็นเรื่อง PATH หรือ permission ของ script — เพิ่ม 2>&1 redirect error ลงไฟล์ log เพื่อดูสาเหตุจริง

แก้ปัญหาที่พบบ่อย

Cron ไม่รัน ทั้งที่ syntax ถูก

ตรวจสอบตามลำดับ:

# 1. ดู cron log
grep CRON /var/log/syslog | tail -20
journalctl -u cron | tail -20

# 2. ดูว่า cron daemon รันอยู่
systemctl status cron

# 3. ตรวจว่า crontab ถูก save
crontab -l

# 4. ลอง script ด้วย user ของ cron ดู (ถ้าใช้ root)
sudo -u root bash /usr/local/bin/myscript.sh

Cron ส่ง output ทาง email ทุกครั้ง แต่ไม่มีใครเห็น

ถ้าไม่ได้ตั้ง mail server ให้ redirect ไป log file หรือทิ้งทั้งหมด:

# Redirect ทั้ง stdout และ stderr ไป log
0 2 * * * /script.sh >> /var/log/myjob.log 2>&1

# ทิ้ง output ทั้งหมด (ถ้าไม่ต้องการ log)
0 2 * * * /script.sh > /dev/null 2>&1

# ส่ง error เท่านั้น ไปที่ log
0 2 * * * /script.sh 2>> /var/log/myjob-errors.log

Cron ใช้ timezone ผิด รันไม่ตรงเวลา

Cron ใช้ system timezone ตรวจสอบ:

timedatectl   # ดู timezone ปัจจุบัน

# ถ้าต้องการให้ cron รันตาม timezone อื่น
CRON_TZ=Asia/Bangkok
0 9 * * * /usr/local/bin/morning-report.sh

Script รันปกติใน terminal แต่ใน cron ได้ output ว่าง

มักเกิดจาก script ต้องการ environment variable เพิ่ม เช่น HOME, USER, หรือ database password ให้กำหนดใน crontab:

MYSQL_PASS=secretpassword
HOME=/root
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

คำถามที่พบบ่อย

Cron กับ Systemd Timer ควรเลือกอะไร?

Cron ง่ายกว่าและติดตั้งมาพร้อม system ทุกตัว Systemd Timer ดีกว่าถ้าต้องการ logging ที่ดี, dependency control หรือ persistent schedule (รัน missed job เมื่อ server กลับมา online) สำหรับ simple daily backup ใช้ cron ก็พอ สำหรับ critical task แนะนำ systemd timer

ทำไม cron ไม่รันตอนเที่ยงคืนตรง ทั้งที่ตั้ง 0 0 * * *?

Cron daemon poll ทุกนาที อาจมี delay เล็กน้อย (1–59 วินาที) เป็นเรื่องปกติ ถ้าต้องการ precision สูงกว่านี้ใช้ systemd timer ที่ตั้ง AccuracySec=1s ได้

จะหยุด cron ที่รันอยู่กลางคันได้อย่างไร?

ต้อง kill process โดยตรง:

ps aux | grep yourscript.sh   # หา PID
kill PID                       # หยุด process

สรุป

ปัญหา cron ไม่รัน 90% มาจาก PATH ไม่ถูกต้อง ใช้ absolute path ทุกคำสั่ง เปิด logging ไว้เสมอ และทดสอบ script ด้วย environment เดียวกับที่ cron จะรัน ถ้าต้องการความน่าเชื่อถือสูงกว่านี้ลองเปลี่ยนมาใช้ systemd timer