กลับไปที่บล็อก

Cron vs systemd timers: ควรใช้แบบไหน

11 กันยายน 2569

บน distro สมัยใหม่ ใช้ systemd timer กับทุกงานที่คุณเป็นเจ้าของ แล้วเก็บ cron ไว้กับงานบรรทัดเดียวของผู้ใช้และเซิร์ฟเวอร์ที่ไม่ใช่คุณตั้งเอง timer เขียน log ทุกการรันลง journal รันทดแทนตารางที่พลาดตอนเครื่องปิดอยู่ได้ และอ้างอิงไฟล์ unit ชุดเดียวกับทุกอย่างในระบบ cron ชนะที่ความกระชับ — บรรทัดเดียวของ crontab -e เอาชนะไฟล์ unit สองไฟล์ — และยังเป็นตัวจัดคิวเวลาตัวเดียวที่การันตีว่ามีอยู่บนคอนเทนเนอร์มินิมอลและเครื่อง Unix แปลกถิ่น ข้อควรระวัง: cron ล้มแบบเงียบ ถ้างานคุณ error ตอนตีสาม cron จะส่งเมลลงกล่อง local ที่ไม่มีใครอ่าน ขณะที่ timer ให้ journalctl -u mytimer.service พร้อม output เต็ม ๆ ด้านล่างมีความต่างที่แท้จริง วิธีทดสอบแต่ละตัว เหตุผลที่ timer อาจไม่ยิง และตารางตัดสินใจ

cron กับ systemd timer ต่างกันอย่างไร?

cron คือ daemon ที่อ่านตารางบรรทัด — ห้าช่องเวลากับหนึ่งคำสั่ง — แล้วรันแต่ละคำสั่งเมื่อเข็มนาฬิกาตรง นั่นคือโมเดลทั้งหมด ไม่มีมโนทัศน์ของงานในฐานะวัตถุ: ไม่มี unit ไม่มีสถานะ ไม่มี dependency ไม่มีรายการ log ของตัวเอง

systemd timer คือไฟล์ unit (foo.timer) ที่ยิงไฟล์ unit อีกตัว (foo.service) เมื่อตารางเวลาตรง งานคือวัตถุชั้นหนึ่งที่มี start, status, logs และการติดตามความล้มเหลวเหมือน service อื่นทุกประการ การตั้งเวลาเป็นได้ทั้งแบบปฏิทิน (สไตล์ cron) หรือแบบ monotonic (OnBootSec=15min ซึ่งการเปลี่ยนเข็มนาฬิกาหลอกไม่ได้)

ผลในทางปฏิบัติ:

  • การเก็บ log: timer เขียน stdout/stderr ลง journal ราย unit; cron ที่ดีที่สุดก็ส่งเมลหา user ในเครื่อง
  • รอบที่พลาด: timer ที่มี Persistent=true จะรันหนึ่งครั้งตอนบูตถ้ารอบมันหายไป; cron แค่ข้าม
  • Dependency: timer รอ network-online.target หรือจุด mount ได้; cron ต้องเขียน retry logic ในสคริปต์เอง
  • ไวยากรณ์: cron คือหนึ่งบรรทัด; timer คือสองไฟล์ นั่นคือค่าใช้จ่ายทั้งหมดของการย้าย

ไวยากรณ์ตารางเวลาแบบไหนง่ายกว่า — crontab หรือ OnCalendar?

ห้าช่องของ cron คือผู้ครองบัลลังก์กระชับ: */15 * * * * คือทุก 15 นาที และแอดมินส่วนใหญ่อ่านมันได้ทั้งที่หลับ OnCalendar= ของ systemd พูดยาวกว่าแต่แสดงออกได้มากกว่าอย่างเด็ดขาด และ systemd-analyze calendar จะบอกรอบถัดไปก่อนที่คุณจะเซฟ — cron ไม่มี dry-run เทียบเท่า

# cron: ทุกวัน 03:30
30 3 * * * /usr/local/bin/backup.sh

# systemd: ตารางเดียวกัน ตรวจสอบได้ก่อนเซฟ
systemd-analyze calendar "*-*-* 03:30:00"
# -> Next elapse: Fri 2026-09-11 03:30:00 ...

OnCalendar จัดการสิ่งที่ cron แสดงออกอย่างสวยงามไม่ได้จริง ๆ: Mon..Fri *-*-* 09..17:00:00 (วันทำงาน เวลาทำการ) หรือ *:0/15 คู่กับ RandomizedDelaySec=10m เพื่อกันเครื่องพันเครื่องยิงเซิร์ฟเวอร์เดียวกันพร้อมกันเป๊ะ

ทดสอบ systemd timer โดยไม่ต้องรออย่างไร?

สามคำสั่งตอบครบ แสดงรายการที่ตั้งเวลาไว้และรอบถัดไป รันงานด้วยมือตรงตามที่ timer จะทำ แล้วอ่าน log ของมัน:

systemctl list-timers --all                 # ทุก timer, รอบถัดไป + รอบล่าสุด
sudo systemctl start backup.service         # ยิงงานตอนนี้ ยูนิตเดียวกับ timer
journalctl -u backup.service -f             # ดู output แบบสด

สังเกตการแยกส่วน: systemctl start backup.timer คือการเอาตารางไปติดอาวุธ; service คืองานจริง ถ้า list-timers โชว์ timer ของคุณ, systemctl status backup.service เป็นสีเขียว และ journal โชว์ output ของคุณ ห่วงโซ่ทั้งเส้นทำงานครบ

รัน cron job ด้วยมืออย่างไร?

cron job รันด้วย environment ฉีกขาด นั่นคือเหตุผลที่ “ในเชลล์ผมทำงาน พอเป็น cron พัง” เป็นแนวบั๊กประเภทหนึ่ง เลียนแบบ cron ให้ตรง ให้รันคำสั่งผ่าน sh ด้วย environment ชุดเดียวกับที่ cron จะใช้:

crontab -l                                  # ยืนยันบรรทัดที่เป๊ะ
env -i SHELL=/bin/sh PATH=/usr/bin:/bin sh -c '/usr/local/bin/backup.sh'
grep CRON /var/log/syslog | tail            # cron ยิงมันจริงไหม? (Debian/Ubuntu)
journalctl -u cron -n 20                    # เหมือนกันบน distro systemd

บรรทัด env -i คือการทดสอบด้วยมือที่ซื่อสัตย์: environment เปล่ากับ PATH ดีฟอลต์ของ cron ความล้มเหลวเงียบ ๆ ของ cron ส่วนใหญ่คือ PATH เปล่าหรือ % ที่ไม่ได้หนีออกในคำสั่ง (cron มอง % เป็นขึ้นบรรทัดใหม่) ทั้งคู่โผล่ทันทีด้วยวิธีนี้

ทำไม systemd timer ของผมถึงไม่ยิง?

สี่สาเหตุครอบคลุมแทบทุกกรณี เรียงตามลำดับที่ควรเช็ก:

  1. ชื่อ service กับ timer ไม่ตรงกัน foo.timer ยิง foo.service — พิมพ์ผิดใน OnFailure หรือ unit ที่ถูกเปลี่ยนชื่อแปลว่า timer “ยิง” เข้าความว่างเปล่า systemctl cat foo.timer โชว์เป้าหมายแบบเป๊ะ
  2. แก้ timer แล้วแต่ไม่ reload หลังแก้ไฟล์ unit: sudo systemctl daemon-reload && sudo systemctl restart foo.timer ไม่มีขั้นนี้ตารางใหม่ของคุณยังไม่มีชีวิต
  3. Persistent=true โดยไม่มีความหมาย OnCalendar อย่างที่คิด — หรือคุณดู systemctl status foo.timer (โชว์ active เสมอแม้นอน) แทนที่จะเป็น list-timers
  4. service ที่มันยิงล้มทันที ทำให้ timer ดูตาย journalctl -u foo.service --since -1h จะโชว์การล่มที่ list-timers ซ่อนไว้

เมื่อดู log จริง ๆ สรุปคำสั่ง journalctl มีตัวกรอง (-u, --since, -f) ที่ทำให้เรื่องนี้เร็ว

Cron vs systemd timers: ตารางตัดสินใจ

cronsystemd timer
การตั้งค่าหนึ่งบรรทัด crontabสองไฟล์ unit
การเก็บ logเมลในเครื่อง มักไม่มีใครอ่านjournal ราย unit
รอบที่พลาด (เครื่องปิด)ถูกข้ามรันหนึ่งครั้งด้วย Persistent=true
Dependency / ลำดับไม่มี — ทำเองในสคริปต์dependency ระดับ unit เต็มรูปแบบ
ดีเลย์แบบสุ่มทำเองด้วย $RANDOMRandomizedDelaySec=
ทดสอบ/dry-run ตารางไม่มีsystemd-analyze calendar
มีอยู่ทุกที่ใช่ — คอนเทนเนอร์ BSD embeddedต้องมี systemd (PID 1)
งานผู้ใช้โดยไม่ใช้ rootcrontab -eunit แบบ systemd --user

กฎนิ้วโป้ง: ปล่อยงานลงเครื่อง systemd ของตัวเอง → timer ตัวเตือนส่วนตัวเร็ว ๆ หรือเครื่องที่คุณไม่ได้สร้าง → cron อะไรที่มี dependency, retry หรือความจำเป็นต้องรู้ว่ามันรันจริง → timer เสมอ รูปแบบที่พบบ่อยคือ timer รันงานบำรุงรักษา — เช่น backup ด้วย rsync รายคืน — ซึ่ง Persistent=true รับประกันว่า backup เกิดขึ้นแม้เครื่องหลับตอนนาทีที่ตั้งเวลา ซึ่ง cron ไม่มีให้เด็ดขาด

— mrsaynothing

— mrsaynothing

บันทึกหน้างานเรื่อง AI, Linux และ self-hosted

คุยต่อโพสต์นี้บน dev.to dev.to ↗

รับวิธีแก้ฉบับถัดไปทางอีเมล

อีเมลหนึ่งฉบับต่อหนึ่งโพสต์ แก้เสร็จแล้วไปต่อ

self-hosted · ไม่มีบุคคลที่สาม · ยกเลิกได้ในคลิกเดียว

นี่คืออะไร?

llama.cpp vs Ollama: ปี 2026 ควรใช้ตัวไหน

ถ้าอ่านแล้วชอบ — ผมสร้างงานแบบนี้เป็นอาชีพ จ้างผม