Trên một distro hiện đại, dùng systemd timer cho mọi thứ bạn sở hữu và giữ cron cho job user một dòng cùng những server bạn không tự dựng. Timer log mọi lần chạy vào journal, có thể chạy bù những lịch bị lỡ khi máy tắt, và phụ thuộc vào cùng loại unit file như mọi thứ khác trên hệ thống. Cron thắng về gọn gàng — một dòng crontab -e hơn hai unit file — và nó vẫn là scheduler duy nhất được đảm bảo tồn tại trên container tối giản và những máy Unix lạ. Cái bẫy: cron hỏng trong im lặng. Nếu job của bạn lỗi lúc 3 giờ sáng, cron gửi thư vào một mailbox local chẳng ai đọc, trong khi timer cho bạn journalctl -u mytimer.service với đầy đủ output. Bên dưới: khác biệt thật sự, cách test mỗi bên, vì sao một timer có thể không kích hoạt, và một bảng quyết định.
cron và systemd timer khác nhau ở điểm nào?
Cron là một daemon đọc một bảng các dòng — năm trường thời gian và một lệnh — và chạy mỗi lệnh khi đồng hồ hệ thống khớp. Đó là toàn bộ mô hình. Nó không có khái niệm job như một đối tượng: không unit, không status, không dependency, không bản ghi log riêng.
Một systemd timer là unit file (foo.timer) kích hoạt một unit file khác (foo.service) khi lịch của nó khớp. Job là một đối tượng hạng nhất với start, status, logs, và theo dõi thất bại như bất kỳ service nào khác. Lập lịch hoặc theo calendar (kiểu cron) hoặc theo monotonic (OnBootSec=15min, thứ mà việc đổi đồng hồ hệ thống không làm nhầm được).
Hệ quả thực tế:
- Log: timer log stdout/stderr vào journal theo từng unit; cron tối đa là gửi thư cho user local.
- Lần chạy bị lỡ: timer có
Persistent=truechạy một lần khi boot nếu lịch bị lỡ; cron chỉ việc bỏ qua. - Dependency: timer có thể chờ
network-online.targethoặc mount point; cron bắt bạn tự viết logic retry trong script. - Cú pháp: cron là một dòng; timer là hai file. Đó là toàn bộ cái giá của việc chuyển.
Cú pháp lịch nào dễ hơn — crontab hay OnCalendar?
Năm trường của cron là chuẩn nhỏ gọn đang ngồi sẵn: */15 * * * * là mỗi 15 phút, và đa số admin đọc được cả khi ngủ. OnCalendar= của systemd dài dòng hơn nhưng biểu đạt mạnh hơn hẳn, và systemd-analyze calendar nói cho bạn biết các lần chạy kế tiếp trước khi bạn chốt — cron không có dry-run tương đương.
# Cron: mỗi ngày lúc 03:30
30 3 * * * /usr/local/bin/backup.sh
# systemd: cùng lịch đó, kiểm chứng được trước khi bạn lưu
systemd-analyze calendar "*-*-* 03:30:00"
# -> Lần chạy kế: Fri 2026-09-11 03:30:00 ... OnCalendar xử lý những thứ cron thật sự không diễn đạt được gọn: Mon..Fri *-*-* 09..17:00:00 (ngày thường, giờ làm việc), hoặc *:0/15 với RandomizedDelaySec=10m để ngăn một nghìn máy dập server cùng một khoảnh khắc.
Làm sao test systemd timer mà không phải chờ?
Ba lệnh trả lời mọi thứ. Liệt kê cái gì được định lịch và khi nào chạy kế tiếp, chạy job bằng tay đúng như timer sẽ làm, rồi đọc log của nó:
systemctl list-timers --all # mọi timer, lần chạy kế + lần trước
sudo systemctl start backup.service # bắn job ngay, cùng unit với timer
journalctl -u backup.service -f # xem output của nó theo thời gian thực Chú ý sự tách biệt: systemctl start backup.timer kích hoạt lịch; service mới là job. Nếu list-timers thấy timer của bạn, systemctl status backup.service màu xanh, và journal hiện output của bạn, thì cả chuỗi đang chạy đúng.
Làm sao chạy cron job bằng tay?
Cron job chạy với môi trường bị lược sạch, vì vậy “chạy trong shell của tôi, hỏng trong cron” là một giống bug riêng. Để tái hiện cron trung thực, chạy lệnh qua sh với đúng môi trường cron sẽ dùng:
crontab -l # xác nhận đúng dòng đó
env -i SHELL=/bin/sh PATH=/usr/bin:/bin sh -c '/usr/local/bin/backup.sh'
grep CRON /var/log/syslog | tail # cron có bắn nó chưa? (Debian/Ubuntu)
journalctl -u cron -n 20 # tương tự, trên distro systemd Dòng env -i đó là bài test thủ công trung thực nhất: một môi trường trống với PATH default của cron. Đa số lỗi cron im lặng là do PATH trống hoặc một % không được quote trong lệnh (cron coi % là ký tự xuống dòng), và cả hai lộ ra ngay theo cách này.
Vì sao systemd timer của tôi không kích hoạt?
Bốn nguyên nhân phủ gần như mọi trường hợp, theo thứ tự cần kiểm tra:
- Tên service và timer không khớp.
foo.timerbắnfoo.service— mộtOnFailuregõ sai hoặc unit bị đổi tên nghĩa là timer “chạy” vào hư không.systemctl cat foo.timercho thấy chính xác nó nhắm vào đâu. - Timer đã bị sửa nhưng chưa reload. Sau khi đổi unit file:
sudo systemctl daemon-reload && sudo systemctl restart foo.timer. Thiếu bước này lịch mới của bạn chưa có hiệu lực. Persistent=truemà không có ngữ nghĩaOnCalendarbạn mong đợi — hoặc bạn đang xemsystemctl status foo.timer(luôn hiện active kể cả khi rảnh) thay vìlist-timers.- Service nó bắn fail ngay lập tức, nên timer trông như chết.
journalctl -u foo.service --since -1hsẽ hiện cú crash màlist-timersche khuất.
Và khi bạn thật sự nhìn vào log, journalctl cheat sheet có đủ filter (-u, --since, -f) để làm việc này nhanh.
Cron vs systemd timer: bảng quyết định
| cron | systemd timer | |
|---|---|---|
| Thiết lập | một dòng crontab | hai unit file |
| Log | thư local, thường không ai đọc | journal, theo từng unit |
| Lần chạy lỡ (máy tắt) | bị bỏ qua | chạy một lần với Persistent=true |
| Dependency / thứ tự | không — tự lo trong script | đủ bộ dependency của unit |
| Delay ngẫu nhiên | tự làm với $RANDOM | RandomizedDelaySec= |
| Test/dry-run lịch | không | systemd-analyze calendar |
| Có mặt khắp nơi | có — container, BSD, embedded | cần systemd (PID 1) |
| Job user không cần root | crontab -e | unit systemd --user |
Kinh nghiệm rút gọn: đưa một job lên máy systemd của bạn → timer. Nhắc việc cá nhân nhanh hoặc một máy bạn không dựng → cron. Bất cứ thứ gì có dependency, retry, hay cần biết nó có chạy thật không → timer, luôn luôn. Một pattern phổ biến là timer chạy job bảo trì — ví dụ backup rsync mỗi đêm — nơi Persistent=true bảo đảm backup vẫn diễn ra dù máy ngủ vào đúng phút định lịch, thứ cron đơn giản không đưa được.
— mrsaynothing
— mrsaynothing
Ghi chú thực địa về AI, Linux và self-hosting.
Thảo luận bài này trên dev.to dev.to ↗
Nhận how-to tiếp theo qua email
Một email mỗi bài viết. Sửa xong rồi đi tiếp.
cái này là gì?llama.cpp vs Ollama: Nên chạy cái nào năm 2026?
Thích kiểu viết này? Tôi build như vậy để kiếm sống. thuê tôi