ใช้ systemd Timer แทน Cron บน VPS Linux
หลายคนคุ้นเคยกับ cron มาตั้งแต่เริ่มใช้ Linux แต่ systemd timer คือทางเลือกที่ทรงพลังกว่า โดยเฉพาะบน VPS สมัยใหม่ที่รัน systemd อยู่แล้ว — จัดการ log ผ่าน journalctl ได้ทันที กำหนด dependency ได้ และควบคุมเวลาได้ละเอียดกว่า
cron vs systemd timer ต่างกันอย่างไร
| ฟีเจอร์ | cron | systemd timer |
|---|---|---|
| Log การทำงาน | ต้องส่งออก stdout เอง | journalctl ดูได้ทันที |
| รันหลัง boot แม้เลยเวลา | ไม่รองรับ | รองรับ (Persistent=true) |
| Dependency | ไม่มี | After=network.target เป็นต้น |
| เวลาสัมพัทธ์ | ไม่รองรับ | OnBootSec, OnUnitActiveSec |
| หยุดชั่วคราว | ต้องลบ crontab | systemctl disable --now |
ส่วนประกอบของ systemd timer
timer หนึ่งชุดประกอบด้วย 2 ไฟล์ใน /etc/systemd/system/:
- service unit (
myapp.service) — กำหนดคำสั่งที่จะรัน - timer unit (
myapp.timer) — กำหนดเวลาที่จะทริกเกอร์ service
สร้าง systemd timer ครั้งแรก
ตัวอย่าง: รัน script backup ทุกวันเวลา 02:00 น.
1. สร้าง service unit
sudo nano /etc/systemd/system/daily-backup.service
[Unit]
Description=Daily backup script
After=network.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal
StandardError=journal
Type=oneshot เหมาะกับงานที่รันครั้งเดียวแล้วจบ (เหมือน cron) ต่างจาก simple ที่ใช้กับ daemon2. สร้าง timer unit
sudo nano /etc/systemd/system/daily-backup.timer
[Unit]
Description=Run daily-backup.service every day at 02:00
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Persistent=true ทำให้งานที่พลาดระหว่าง server ปิดอยู่จะรันทันทีหลัง boot — ป้องกัน backup หายเงียบๆ3. เปิดใช้งานและสั่งรัน
# โหลด unit ใหม่
sudo systemctl daemon-reload
# เปิด timer ให้ทำงานอัตโนมัติและสั่งรันทันที
sudo systemctl enable --now daily-backup.timer
# ดูสถานะ
sudo systemctl status daily-backup.timer
รูปแบบ OnCalendar ที่ใช้บ่อย
| ความต้องการ | OnCalendar |
|---|---|
| ทุกชั่วโมง | hourly |
| ทุกวัน 03:00 | *-*-* 03:00:00 |
| ทุกจันทร์ 09:00 | Mon *-*-* 09:00:00 |
| ทุกวันที่ 1 ของเดือน | *-*-01 00:00:00 |
| ทุก 5 นาที | *:0/5 |
| ทุก 30 วินาที | OnUnitActiveSec=30s |
timer หลัง boot (OnBootSec)
ถ้าต้องการให้รันหลัง server boot โดยไม่สนเวลานาฬิกา ใช้ OnBootSec:
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
รูปแบบนี้หมายถึง: รันครั้งแรก 5 นาทีหลัง boot จากนั้นรันซ้ำทุก 1 ชั่วโมง
รัน timer ในฐานะ user ปกติ (user timer)
ไม่จำเป็นต้องใช้ root เสมอ — สร้าง user timer ที่ไม่ต้องการ sudo:
# สร้างไดเรกทอรีสำหรับ user timer
mkdir -p ~/.config/systemd/user/
# สร้าง service
nano ~/.config/systemd/user/myapp-task.service
[Unit]
Description=My app periodic task
[Service]
Type=oneshot
ExecStart=/home/user/scripts/run-task.sh
# สร้าง timer
nano ~/.config/systemd/user/myapp-task.timer
[Unit]
Description=Run myapp-task every 6 hours
[Timer]
OnCalendar=*-*-* 00/6:00:00
Persistent=true
[Install]
WantedBy=timers.target
# เปิดใช้งาน (ไม่ต้องใช้ sudo)
systemctl --user daemon-reload
systemctl --user enable --now myapp-task.timer
systemctl --user list-timers
sudo loginctl enable-linger usernameดูรายการ timer ทั้งหมด
# ดู timer ทั้งหมดในระบบ
systemctl list-timers --all
# ดูเฉพาะที่ active
systemctl list-timers
# ดู timer ของ user
systemctl --user list-timers
ตรวจสอบว่า systemd timer ทำงานถูกต้อง
# ดูสถานะ timer
systemctl status daily-backup.timer
# ดูว่า service รันสำเร็จครั้งล่าสุดเมื่อไหร่
systemctl status daily-backup.service
# ดู log ของ service
journalctl -u daily-backup.service -n 50
# ดู log ย้อนหลัง 24 ชั่วโมง
journalctl -u daily-backup.service --since "24 hours ago"
# ทดสอบรัน service ทันทีโดยไม่รอ timer
systemctl start daily-backup.service
systemd-analyze calendar "*-*-* 02:00:00" เพื่อตรวจว่า expression ที่เขียนถูกต้องและดูว่าจะ trigger ครั้งถัดไปเมื่อไหร่แก้ปัญหาที่พบบ่อย
Error: "Failed to enable unit: Unit file daily-backup.timer does not exist"
เกิดจาก systemd ยังไม่โหลด unit file ใหม่ หรือชื่อไฟล์ผิด
# โหลด unit ใหม่ก่อนเสมอ
sudo systemctl daemon-reload
# ตรวจว่าไฟล์อยู่ถูกที่
ls -la /etc/systemd/system/daily-backup.*
# ตรวจ syntax ของ unit file
systemd-analyze verify /etc/systemd/system/daily-backup.timer
Error: "daily-backup.service: Main process exited, code=exited, status=127"
Exit code 127 หมายถึงหา command ไม่เจอ — script path ผิดหรือไม่มี execute permission
# ตรวจว่า script มีอยู่และรันได้
ls -la /usr/local/bin/backup.sh
chmod +x /usr/local/bin/backup.sh
# ตรวจ path ใน ExecStart ต้องเป็น absolute path
# ผิด: ExecStart=backup.sh
# ถูก: ExecStart=/usr/local/bin/backup.sh
Error: timer ไม่รันแม้ถึงเวลา
ตรวจสอบว่า timer active และเวลาระบบถูกต้อง
# ตรวจว่า timer active
systemctl is-active daily-backup.timer
# ตรวจเวลาระบบ (ต้องตรง timezone ที่ต้องการ)
timedatectl status
# ดู next trigger time
systemctl list-timers daily-backup.timer
Error: "daily-backup.service: Skipped due to already running"
เกิดเมื่อ service ก่อนหน้ายังรันอยู่ไม่เสร็จ timer trigger ซ้ำ
# เพิ่ม Type=oneshot และ RemainAfterExit ใน service
[Service]
Type=oneshot
RemainAfterExit=no
# หรือตรวจว่ามี stale process ค้างอยู่
systemctl status daily-backup.service
systemctl reset-failed daily-backup.service
Error: "Condition check resulted in daily-backup.service being skipped"
มี ConditionPathExists หรือ Condition อื่นที่ไม่ผ่าน
# ดู condition ที่กำหนดไว้
systemctl cat daily-backup.service
# ตรวจ log เพื่อดู condition ที่ fail
journalctl -u daily-backup.service -n 20 --no-pager
คำถามที่พบบ่อย
systemd timer กับ cron อันไหนเหมาะกับ VPS มากกว่า?
สำหรับ VPS ที่ใช้ Ubuntu/Debian/CentOS สมัยใหม่ systemd timer เหมาะกว่า เพราะ log รวมอยู่ใน journald ไม่ต้องกำหนด output แยก และ Persistent=true ป้องกันงานหายถ้า server restart กลางดึก — cron ยังใช้ดีถ้าต้องการ syntax สั้นๆ หรือต้องใช้ร่วมกับระบบเก่า
จะดู log ของงานที่รันผ่าน systemd timer ได้อย่างไร?
ใช้ journalctl -u ชื่อ-service.service — ดูได้ทันทีโดยไม่ต้องกำหนด log file เอง เพิ่ม -f เพื่อดูแบบ real-time หรือ --since "1 hour ago" เพื่อกรองช่วงเวลา
timer สามารถรันคำสั่งหลายอันพร้อมกันได้ไหม?
ใน service unit หนึ่งไฟล์ใส่ ExecStart ได้หลายบรรทัด (ต้องมี Type=oneshot) หรือสร้าง script รวมคำสั่งทั้งหมดแล้วชี้ ExecStart ไปที่ script นั้น — ทำให้ log ครบทุกคำสั่งในที่เดียว