- systemd timer là lựa chọn thay thế thực dụng cho
cron, dùng để chạy các unit như .service theo lịch và giúp quản lý lịch sử, đầu ra, môi trường rõ ràng hơn
cron truyền thống có các điểm yếu như $PATH mơ hồ, stdout/stderr dễ bị thất lạc, lịch sử chạy khó theo dõi, và cú pháp lịch khó đọc
- Timer liên kết
.timer và .service cùng stem, và dùng OnCalendar, OnBootSec, OnUnitActiveSec để biểu đạt việc chạy theo thời điểm hoặc theo sự kiện
- Có thể dùng
systemd-analyze calendar và systemctl list-timers để kiểm tra biểu thức thời gian và thời điểm chạy tiếp theo; WakeSystem= có thể đánh thức máy để chạy ngay cả khi đang ngủ
RandomizedOffsetSec và FixedRandomDelay= giúp giảm đỉnh chạy đồng thời, còn Persistent= sẽ bù ngay sau khi máy trực tuyến trở lại cho các lần chạy bị lỡ khi timer không hoạt động
Vì sao dùng systemd timer để thay cron
cron job là cách gọi phổ biến để chỉ thành phần cơ bản trong điện toán dùng để chạy tác vụ theo lịch như “chạy việc này mỗi ngày”, “chạy việc kia mỗi tháng”, kể cả khi tác vụ đó không do daemon cron thực sự thực hiện
- systemd timer là một unit của systemd dùng để chạy một unit khác, thường là
.service, theo lịch cụ thể, và có thể trở thành lựa chọn thay thế về mặt chức năng cho daemon cron truyền thống
cron truyền thống có một số điểm yếu trong thực tế vận hành
- Thiết lập
$PATH mơ hồ khiến kết quả chạy script khó dự đoán
- Đầu ra
stdout và stderr thường rơi vào hố đen hoặc bị gửi vào hệ thống mail của máy chủ
- Khó theo dõi và truy vấn lịch sử thực thi
- Cú pháp lịch như
01,31 04,05 1-15 1,6 * không dễ đọc hay trực quan với con người
- systemd timer giảm bớt các vấn đề này đồng thời vẫn cung cấp cấu hình lịch kiểu lịch tương tự
cron
Cấu trúc cơ bản: service và timer
- systemd timer cần một đối tượng để chạy, và unit
.service về mặt logic có thể xem như một script
- Ví dụ, nếu đặt unit sau tại
/etc/systemd/system/roulette.service thì sẽ cài một service có xác suất 1 trên 10 tắt máy tính
[Unit]
Description=1 in 10 chance to break your chains
[Service]
ExecStart=/usr/bin/env bash -c '[[ $(($RANDOM % 10)) == 0 ]] && systemctl poweroff || echo LIVE ANOTHER DAY'
ExecCondition= là cách tích hợp hơn để biểu đạt việc chạy có điều kiện bằng tùy chọn service của systemd, đồng thời thể hiện rõ hơn ở cấp unit rằng “có nên tiếp tục chạy hay không?”
[Unit]
Description=1 in 10 chance to break your chains
[Service]
ExecCondition=/run/current-system/sw/bin/bash -c '[[ $(($RANDOM % 10)) == 0 ]]'
ExecStart=/run/current-system/sw/bin/systemctl poweroff
- Nếu điều kiện không thỏa, journal sẽ ghi lại thông báo rõ ràng hơn
May 05 11:05:32 diesel systemd[3117]: Condition check resulted in 1 in 10 chance to break your chains being skipped.
- Nhìn chung, tận dụng các tùy chọn systemd cung cấp thường mang lại trải nghiệm tốt hơn so với tự viết script
OnFailure= có thể dùng khi cần phản ứng với việc script service thất bại
Restart= có thể dùng để thử phục hồi khỏi lỗi tạm thời
Liên kết unit timer và thực thi
- Đặt
/etc/systemd/system/roulette.timer với cùng stem tên file sẽ giúp liên kết timer với roulette.service
[Unit]
Description=impending destruction
[Timer]
OnCalendar=10:00
[Install]
WantedBy=timers.target
- Mặc định, thiết lập
Unit= của timer sẽ chọn unit service cùng stem với hậu tố .service
- Trong ví dụ này,
roulette.service sẽ được chọn
- Nếu muốn chạy một unit service tên khác, có thể đổi
Unit=
- Đối tượng trong
ExecStart= mặc định không được chạy như lệnh shell
- Đích có đường dẫn tuyệt đối cần được xử lý như script hoặc như trình thông dịch nhận script dưới dạng đối số chuỗi
ExecStart=/usr/bin/echo Hello | /usr/bin/awk sẽ không hoạt động trong ngữ cảnh này vì pipe không có ý nghĩa
- Các đối số của
ExecStart= mặc định không kế thừa biến môi trường ngoài một số giá trị mặc định của system manager
$PATH mặc định gần như trống
- Chạy
/usr/bin/env là một biện pháp bảo vệ đơn giản để các mục như systemctl vẫn dùng được
- Nếu chỉ dùng
ExecStart=/usr/bin/bash thì $PATH sẽ có các mục mặc định, nhưng dùng env là lớp an toàn bổ sung
- Có thể chạy service trực tiếp mà không cần timer
systemctl start roulette
- Service không có phần
[Install] thì không thể enable; trong cấu trúc này, timer là cách chuẩn để chạy service một cách nhất quán
systemctl mặc định vẫn thao tác với roulette.service ngay cả khi không ghi hậu tố rõ ràng
- Áp dụng
systemctl start cho unit .timer sẽ đưa timer vào trạng thái hoạt động, nhưng không chạy ngay service đích trong Unit=
systemctl start roulette.timer
status sẽ hiển thị lần tiếp theo timer sẽ chạy
systemctl status roulette.timer
Trigger: Sat 2026-04-18 10:00:00 MDT; 35min left
- Luồng đơn giản nhất là tạo service đích, đặt timer có lịch ở cùng vị trí, rồi khởi động timer thay vì khởi động target
- Nếu
[Install] của unit timer có WantedBy= thì cũng có thể để timer tự lên cùng lúc khởi động máy
systemctl enable roulette.timer
Biểu đạt thời gian: sự kiện lịch và khoảng thời gian
- Trong timer, cách biểu đạt lịch là điều quan trọng, và cần phân biệt giữa khoảng thời gian lặp lại với sự kiện lịch hoặc timestamp
- Trang manual
systemd.time(7) có nhiều ví dụ hữu ích và đáng là tài liệu đầu tiên để tham khảo khi viết timer
systemd-analyze có thể kiểm tra và diễn giải biểu thức thời gian
systemd-analyze calendar '*-*-* *:*:*'
Normalized form: *-*-* *:*:*
Next elapse: Sat 2026-04-18 16:44:26 MDT
(in UTC): Sat 2026-04-18 22:44:26 UTC
From now: 431ms left
- systemd timer không chỉ hỗ trợ thời điểm lặp lại theo đồng hồ thực, mà khác với
cron truyền thống, còn cho phép định nghĩa khoảng thời gian lặp lại dựa trên một sự kiện trước đó
- Dạng đầy đủ của
daily có nghĩa là chạy lúc 00:00:00 mỗi năm, mỗi tháng, mỗi ngày
*-*-* 00:00:00
│ │ │ │ │ ╰── at second 00
│ │ │ │ ╰───── at minute 00
│ │ │ ╰──────── at hour 00
│ │ ╰────────── every day
│ ╰──────────── every month
╰────────────── every year
- Có thể dùng các dạng viết tắt như
daily, dạng đầy đủ, và các giá trị khác được systemd.time(7) hỗ trợ; systemd-analyze giúp kiểm chứng các giả định đó
Khi chạy theo sự kiện phù hợp hơn
- Trong công việc thực tế, nhiều khi cách diễn đạt “chạy sau một sự kiện khác” phù hợp hơn là “chạy mỗi ngày vào cùng một giờ”
- Với tác vụ dọn thư mục tạm, nếu biểu thức
cron ngay sau lúc khởi động đã trôi qua thì /tmp có thể hầu như chẳng có gì để dọn
- Diễn đạt là “chạy sau một giờ kể từ khi máy khởi động, rồi sau đó chạy mỗi giờ” sẽ khớp hơn với hành vi thực của service và logic lịch
[Timer]
OnBootSec=1h
OnUnitActiveSec=1h
OnBootSec=1h nghĩa là chạy một lần sau 1 giờ kể từ lúc máy khởi động
OnUnitActiveSec=1h nghĩa là chạy lại sau 1 giờ kể từ khi Unit= đã được thực thi, khiến timer ngầm tiếp tục lặp mãi
- Kiểu biểu đạt chu kỳ như vậy thường phù hợp hơn cho các tác vụ “thỉnh thoảng chạy một lần” so với kiểu “chạy vào phút này của mỗi giờ”
- Trong ví dụ bot Slack polling API của Advent of Code, biểu thức cron
*/15 đúng với chính sách “mỗi 15 phút” của API, nhưng nếu ai cũng polling như nhau thì có thể gây dồn lưu lượng
- Nếu khởi động timer sau khi sửa mã và để nó chạy mỗi 15 phút kể từ đó, vẫn đáp ứng yêu cầu mà còn có khả năng giảm vấn đề thundering herd
Xem trạng thái timer trong nháy mắt
systemctl list-timers là lệnh cấp cao giúp tóm tắt tình trạng timer trên một máy
systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2026-04-20 15:15:00 MDT 1min 40s Mon 2026-04-20 15:00:05 MDT 13min ago zfs-snapshot-frequent.timer zfs-snapshot-frequent.service
Mon 2026-04-20 15:32:16 MDT 18min Mon 2026-04-20 14:22:15 MDT 51min ago fwupd-refresh.timer fwupd-refresh.service
Mon 2026-04-20 16:00:00 MDT 46min Mon 2026-04-20 15:00:05 MDT 13min ago logrotate.timer logrotate.service
Mon 2026-04-20 16:00:00 MDT 46min Mon 2026-04-20 15:00:05 MDT 13min ago zfs-snapshot-hourly.timer zfs-snapshot-hourly.service
Tue 2026-04-21 00:00:00 MDT 8h Mon 2026-04-20 09:43:22 MDT 5h 29min ago zfs-snapshot-daily.timer zfs-snapshot-daily.service
Tue 2026-04-21 07:31:28 MDT 16h Sun 2026-04-19 20:15:47 MDT 7h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2026-04-27 00:00:00 MDT 6 days Mon 2026-04-20 09:43:22 MDT 5h 29min ago zfs-snapshot-weekly.timer zfs-snapshot-weekly.service
Mon 2026-04-27 01:09:27 MDT 6 days Mon 2026-04-20 09:43:22 MDT 5h 29min ago fstrim.timer fstrim.service
Mon 2026-04-27 04:28:38 MDT 6 days Mon 2026-04-20 09:43:22 MDT 5h 29min ago zpool-trim.timer zpool-trim.service
Fri 2026-05-01 00:00:00 MDT 1 week 3 days Wed 2026-04-01 10:07:51 MDT 1 week 1 day ago zfs-snapshot-monthly.timer zfs-snapshot-monthly.service
Fri 2026-05-01 03:17:17 MDT 1 week 3 days Wed 2026-04-01 10:07:51 MDT 1 week 1 day ago zfs-scrub.timer zfs-scrub.service
11 timers listed.
Pass --all to see loaded but inactive timers, too.
- Chỉ với một lệnh là có thể nhìn toàn cảnh các mục đang chạy theo lịch timer
list-timers là một phần của nhóm lệnh con systemd thường dùng
list-units cũng hữu ích
list-paths là lệnh con mới được thêm vào systemctl gần đây hơn
Đánh thức máy đang ngủ để chạy
WakeSystem= có thể cho phép timer khi đến hạn sẽ đánh thức hệ thống khỏi trạng thái ngủ
WakeSystem=
Takes a boolean argument. If true, an elapsing timer will
cause the system to resume from suspend, should it be
suspended and if the system supports this.
...
- Tính năng này hữu ích khi cần chạy một script quan trọng mà không cần hành động vật lý như người dùng mở nắp laptop
- Trên các bản phân phối như Arch hay NixOS vốn hỗ trợ tải trước gói cập nhật trước khi dùng, có thể tải gói cập nhật vào đêm khuya rồi sáng hôm sau cập nhật khi đã ngồi trước bàn phím
- Manual có nói rằng nếu muốn hệ thống ngủ lại sau khi
.service kết thúc thì cần tự xử lý việc đưa máy về chế độ ngủ lần nữa
Phân tán thời điểm chạy và giảm thundering herd
- Vấn đề thundering herd là vấn đề hệ thống xảy ra khi nhiều tiến trình cùng thức dậy một lúc
- Nếu toàn bộ hệ thống Debian trên thế giới đều bị hardcode chạy
apt update lúc 00:00:00, thì nửa đêm sẽ trở thành thời điểm đỉnh lưu lượng tệ hại cho tất cả
FixedRandomDelay= và RandomizedOffsetSec= giúp phân tán thời điểm thực thi
FixedRandomDelay=
Takes a boolean argument. When enabled, the randomized delay
specified by RandomizedDelaySec= is chosen deterministically,
and remains stable between all firings of the same timer,
even if the manager is restarted. ...
RandomizedOffsetSec=
Offsets the timer by a stable, randomly-selected, and evenly
distributed amount of time between 0 and the specified time
value. ...
- Có thể dùng các thiết lập này trên hệ thống thực tế để kiểm tra cập nhật phần mềm
- Việc phân bố thời điểm chạy theo phân phối đều giúp giảm vấn đề thundering herd, làm hành vi nhất quán hơn, và tránh các yếu tố gây nhiễu như daemon khởi động lại trong lúc đang điều phối dịch vụ phân tán
- Nhìn chung, các tùy chọn về thời gian rất giàu khả năng cấu hình và cho phép kiểm soát chi tiết
Bù ngay các lần chạy bị lỡ
Persistent= đặc biệt phù hợp với các script theo lịch không nên bị bỏ qua do laptop đang ngủ, nhưng cũng chưa cần đến WakeSystem=
Persistent=
Takes a boolean argument. If true, the time when the service
unit was last triggered is stored on disk. When the timer is
activated, the service unit is triggered immediately if it
would have been triggered at least once during the time when
the timer was inactive. ...
- Nếu một hệ thống có lịch check-in quản lý cấu hình gặp downtime, chỉ cần bật
Persistent= trong .timer là có thể hội tụ lại trạng thái đúng ngay sau khi máy trực tuyến trở lại
- Nếu không có
Persistent=, có thể phải chờ đến thời điểm chạy bình thường tiếp theo của timer, và khoảng chờ đó có thể rất dài
- Những tác vụ khác cũng không nên chờ sau khi phát hiện lỡ kích hoạt gồm cập nhật hệ thống, kiểm tra batch job, v.v.
Những điểm cần chú ý khi viết timer
- Timer trong ngữ cảnh user manager được quản lý bằng
systemctl --user vẫn hoàn toàn hợp lệ, nhưng cần chú ý target được ghi trong [Install]
- Tùy bản phân phối, target phù hợp cho user timer có thể là
default.target
- Cũng như
cron, lưu ý chung về việc phải giữ đồng hồ hệ thống chính xác vẫn hoàn toàn áp dụng
- Người dùng systemd có thể kiểm tra trạng thái đồng bộ bằng
timedatectl timesync-status
- Nhiều trình soạn thảo hỗ trợ sẵn định dạng file unit của systemd, rất hữu ích khi file unit bắt đầu dài ra
- Trên Emacs có thể dùng gói emacs systemd
2 bình luận
Ý kiến trên Hacker News
Tôi chưa dùng systemd timer đủ nhiều để phản biện mạnh, nhưng không rõ lắm nhận định rằng $PATH của cron mơ hồ
Bạn có thể đặt
PATHngay trong crontab, nên tôi không thấy lý do gì để cho rằng nó khó đoán hơn các giá trị được thiết lập ở những nơi như/etc/bashrc,~/.bashrc,~/.profile,~/.bash_profile,/etc/systemd/…Tôi dùng Linux từ năm 1994 nhưng không thuộc lòng cú pháp cron. Dù vậy, trong chú thích của crontab đã có sẵn
m h dom mon dow command, nên chỉ cần điền số vào đúng tiêu đề là đượcNhững phàn nàn còn lại thì tôi hiểu, và lần tới nếu cần một tác vụ cron, tôi định sẽ thử systemd timer
Còn có dấu phẩy, dấu gạch chéo, dấu sao và các tổ hợp của chúng; nếu cần ngẫu nhiên hóa thì thường cron tự thân không làm được nên phải nhét vào trong lệnh. Việc viết một đặc tả cron không tầm thường là không hề dễ
Khi ai đó nhập thứ như
5,3/4 4-8,11 1 4,5,6,9-11 */2, bạn sẽ có niềm vui phải reverse-engineer xem họ thực sự định làm gì, mà thường thì nó không mang nghĩa như họ đã viếtNếu thêm cả các phần mở rộng chỉ được hỗ trợ trong một số môi trường cron thì còn phức tạp hơn nữa
systemd timer dễ quản lý hơn nhiều. Khả năng kiểm soát việc có cho phép chạy chồng lặp hay không với các tác vụ chạy lâu, hoặc chạy tác vụ trong một khoảng từ lúc bắt đầu đến lúc kết thúc thay vì tại một thời điểm cố định, là những cải tiến lớn
Trước đây tôi từng có một tác vụ sao lưu mắc vào vòng lặp symbolic link khiến VPS sập, và cron vẫn tiếp tục khởi tạo các tác vụ sao lưu mới dù bản sao lưu trước còn chưa xong
Việc phải viết lại lệnh và script do
PATHđặc thù của CRON cũng rất bất tiện, dù ở một số systemd timer cũng có thể tương tự. Dù vậy, ít nhất bạn có thể chạy timer thủ công mà không cần sửa crontab để nó chạy sau 30 giây rồi ngồi chờ$PATHtrong crontab chỉ để test tác vụ cronNếu không hard-code thì
$PATHkhi cron thực thi sẽ khác với$PATHlúc bạn tự chạy thử lệnh.systemctl start foo.servicekhởi chạy lệnh trong cùng môi trường với khi timer kích hoạt, nên bạn biết nó sẽ hoạt động theo cùng cách hay khôngNgược lại, tác vụ cron sẽ chạy vào thời điểm ghi trong crontab. systemd timer thì phần lớn sẽ kích hoạt vào thời điểm chỉ định, nhưng do lỗi logic của systemd, có thể nó chạy một lần vào ngày 29 tháng 2 rồi không bao giờ chạy lại nữa, hoặc khi bạn “restart” timer unit thì có thể kích hoạt hoặc không
Trong systemd, bất kể thứ gì kích hoạt thì unit thực sự được chạy vẫn là cùng một unit, nên không có khoảng cách đó
Bạn vẫn cần biết môi trường mặc định là gì, nhưng đó là một môi trường gần như sạch và không bị shell tác động. Tôi xem đây là ưu điểm của systemd
PATHlà mơ hồ, nhưng cron đúng là có vấn đề liên quan đếnPATHGiá trị mặc định thiếu những thứ mà người ta dễ mong đợi như
/usr/local/binhay/usr/sbincho root, và một số bản phân phối như Arch Linux thậm chí không ghi đường dẫn mặc định hay khuyến nghị cấu hình trong trang hướng dẫnNếu chỉ muốn thêm đường dẫn cho một script cụ thể thì bạn phải bọc bằng
env, hoặc cấu hình trong wrapper script, hoặc đổi đường dẫn trước mục đó rồi đổi lại sauBạn không thể dùng
~hay$HOMEtrongPATHmà phải viết toàn bộ đường dẫn tuyệt đối, nên với crontab của người dùng thì đặc biệt phiềnKhông phải là khó lách qua, nhưng mặc định dùng cùng đường dẫn với các dịch vụ khác thì systemd timer vẫn mang lại trải nghiệm tốt hơn
Tôi chuyển từ cronie sang systemd timer vì nó linh hoạt hơn với thời điểm hệ thống khởi động
Chiến lược sao lưu của tôi là tạo các mục lưu trữ borg vào một giờ cố định mỗi ngày, nhưng cronie đòi hỏi hệ thống phải đang bật đúng lúc đó. systemd timer chịu được tình huống này và sẽ chạy dịch vụ ngay khi hệ thống sẵn sàng dùng
Kho lưu trữ tự động hóa sao lưu là https://github.com/gchamon/borg-automated-backups
/etc/cron.hourly/0anacronmỗi giờ, và dù có lỡ thời điểm chạy sớm nhất thì nó vẫn thực hiện các tác vụ trong/etc/cron.{daily,weekly,monthly}. Bạn cũng có thể đặt độ trễ ngẫu nhiênNếu chỉnh
/etc/anacrontabthì có thể tạo lịch tùy chỉnhỞ cấp người dùng, có lẽ bạn có thể thêm một mục như
@hourly anacron -t /path/to/anacrontab -S /path/to/spooldirvào crontab của người dùng, dù tôi chưa tự thửNhiều triển khai cron cũng có cơ chế tương tự
@reboot, tôi dùng nó cho vài script và nó hoạt động tốt@rebootsao?Không tin lắm vào đầu phun máy in Canon vì nếu để không một thời gian thì nó có thể bị tắc. Thế là tôi bảo Claude viết script systemd để mỗi tuần in ra ảnh chó con, đồng thời đảm bảo có đủ phổ CMYK để máy in phải hoạt động thật sự
Mỗi sáng thứ Hai ngồi vào bàn mà thấy máy in đột nhiên nhả ra một tấm ảnh thì cũng là một kiểu bất ngờ khá vui
Ít ra cũng có thể thành ý tưởng cho dự án hội thi khoa học của bọn trẻ
Sau khi ông mất, nó bị bỏ không vài năm và còn bị ngắt điện hẳn, nhưng lúc cần in màu thì tôi chỉ cắm điện rồi in luôn
Đến khoảng 1/5 trang đầu tiên thì mọi màu đều trở lại, và sau đó nó in thêm khoảng 20 trang nữa mà không vấn đề gì
Có vẻ giống vấn đề phần mềm hơn là phần cứng: nếu để máy in nối với máy tính mà không dùng hơn một tuần thì nó cứ thế ngừng in
Tôi có thể đã đào log để tìm nguyên nhân, nhưng thay vào đó lại tạo một cron job in trang kiểm tra vào thứ Hai và thứ Năm. Ở đầu trang kiểm tra chỉ có dòng như
LOL PRINTER WORKSThực ra cũng không lãng phí như tưởng tượng. Tôi học khá nhiều môn toán nên cần rất nhiều giấy in một mặt để giải bài; tôi dùng các bản in lỗi hoặc bản in thử trước, rồi mới dùng giấy trắng
Độ phân giải tối đa chỉ 600dpi nhưng màu rất đẹp và đồng đều, và việc toner không bị khô là tiêu chí mua hàng cốt lõi của anh tôi. HP inkjet của anh ấy đã bị tắc nhiều lần
Tôi cực kỳ thích systemd timer. Tôi đã dần chuyển toàn bộ các cron job từng triển khai bằng Ansible sang timer, và giờ chỉ cần Ansible copy là xong
Tích hợp với
journalctlđặc biệt tốt, và còn tốt hơn trên các OS mới nơi syslog biến mất, như Debian 13. Việc có thể khởi chạy service thủ công để debug cũng rất tiệnCron job bị lỗi trước đây thường là kiểu phiền phức phải copy-paste hoặc viết thêm shell script, chưa kể hố đen nuốt mất standard output của cron job
Cũng có thể giám sát như các service systemd bình thường và nhận cảnh báo khi thất bại. Tôi cũng đánh giá cao việc các dự án mã nguồn mở ngày càng khuyến nghị timer như một cách triển khai
NixOS mặc định dùng systemd nên coi nó như thành phần hạng nhất trong quản trị. Điều này đặc biệt hay nếu trước đó bạn từng dùng launchd trên macOS
Tôi cũng thích việc khi phân phối công cụ cho NixOS, có thể tận dụng systemd một cách tự nhiên chứ không phải gắn thêm như miếng vá tạm
Tuy vậy, nếu phải phân phối công cụ có nhiều quản lý vòng đời cho toàn bộ người dùng Linux, tôi vẫn băn khoăn nên làm sao vì systemd không phải ở đâu cũng có
Tôi đang chạy scrub hàng tháng cho pool btrfs bằng systemd timer. Nó khá hữu ích vì có thể quyết định liệu khi người dùng tự chạy scrub thì có bỏ qua lịch tiếp theo không, hoặc nếu là tác vụ hàng tháng mà thiết bị đã tắt 6 tháng thì sẽ dồn backlog hay gộp lại thành một lần chạy
systemd.services.sync-recyclarr = { serviceConfig.Type = "oneshot"; path = [ pkgs.podman ]; script = '' podman exec -it recyclarr recyclarr sync radarr podman exec -it recyclarr recyclarr sync sonarr ''; };systemd.timers.sync-recyclarr = { timerConfig = { OnCalendar = "daily"; Persistent = true; Unit = "sync-recyclarr.service"; }; partOf = [ "sync-recyclarr.service" ]; requires = [ "podman-recyclarr.service" ]; wantedBy = [ "timers.target" ]; };flake.nixkhôngTôi cũng dùng NixOS, nhưng giữ toàn bộ cấu hình ở định dạng gốc rồi chỉ dùng Nix để tạo symbolic link. Làm vậy thì cũng dễ mang cấu hình sang các hệ thống non-NixOS hơn
Vấn đề là NixOS có vẻ không nhận và chạy các systemd timer cùng service đặt trong thư mục
~/.config/systems/user, và những thứ nhưWantedBy=default.targetdường như cũng không có tác dụngVì thế sau khi phải tự khởi động lại mọi service bằng tay sau mỗi lần reboot, tôi mới thấy mình đồng ý rằng systemd timer đúng là tuyệt vời
Timer không nhất thiết chỉ chạy với service unit cùng tên mà còn có thể làm việc với các unit tùy ý, nên linh hoạt hơn mong đợi
Trên server của tôi có một timer khởi chạy
backup.target, thứ sẽ chạy toàn bộ chu trình sao lưu gồmrestic backup,restic prune,restic forget, kèm thời điểm bắt đầu ngẫu nhiên mỗi sáng và thông báoCác unit
restic-*thực tế là Podman Quadlet, nên miễn là server có Podman và Systemd thì cấu hình đó vẫn chạy được bất kể nội dung cụ thể trên server là gìTuy vậy, timer là một trong những kiểu unit systemd thô ráp nhất để dùng hằng ngày. Tôi hiểu vì sao nó tách làm hai file và cú pháp start với enable lại khác nhau, nhưng đôi khi tôi chỉ muốn tạo một file, chạy script rồi xong
Trong thời đại LLM thì có thể đây không còn là vấn đề lớn, nhưng nó vẫn cho cảm giác hơi phiền một chút mỗi lần làm
Dù không phải lúc nào tôi cũng cực kỳ thích mọi khía cạnh của systemd, nhưng nhìn chung tôi đồng ý với cảm nhận này
Với các tác vụ theo lịch ở cấp độ “hệ thống”, tôi gần như đã bỏ hẳn cron và chuyển sang ưu tiên systemd timer. Với việc lập lịch trong phạm vi một ứng dụng cụ thể, tôi có thể dùng thứ như Quartz
Lý do thì khá mơ hồ và khó giải thích, nhưng cách làm của systemd khớp gọn gàng hơn với mô hình trong đầu tôi về kiểu “mọi thứ nên hoạt động như thế này”. Trước đây tôi cũng từng gặp khá nhiều trường hợp việc chạy script bằng cron có
PATHmơ hồ nên khó đoán trước, dù đó không phải là tất cảTôi không định khẳng định systemd timer phổ quát hay khách quan mà nói là tốt hơn cron, nhưng ít nhất nó đã thuyết phục được tôi
Đây là phần nhập môn về systemd timer rất hay, đủ thuyết phục để cuối cùng tôi cũng muốn thử dùng
Tôi cũng thích
list-timers. Với cron, không dễ để nắm được toàn bộ các tác vụ cron đang chạy trên một máy chỉ trong một cái nhìn. Phải kiểm tra crontab của mọi người dùng,/etc/cron.d/, cùng các thư mục daily/hourly/monthlyThực tế tôi có một trường hợp cần chạy một lần vào khoảng 5 phút sau khi khởi động, rồi sau đó khoảng mỗi 12 giờ lại chạy tiếp, nên thật tốt khi biết systemd timer xử lý được việc này
systemd-analyzevàsystemctl list-timersthực sự rất đáng giásystemd lúc mới nhìn thì phức tạp, nhưng dùng rồi thì không còn muốn dùng thứ khác nữa. Quản lý mọi thứ bằng
systemctlrất tiệnVí dụ, tôi không hiểu vì sao định nghĩa unit nhất thiết phải là các tệp thật trên đĩa. Khi nạp lại daemon, nó đọc lại toàn bộ tệp chứ không chỉ những tệp đã thay đổi. Tôi vẫn nghĩ lẽ ra nên có một API để thêm unit theo cách lập trình được. Có vài thứ tương tự, nhưng bị ràng buộc nhiều và không linh hoạt
Việc không thể khai báo nhiều unit trong một tệp cũng khá khó hiểu. Thiết kế xoay quanh hệ thống tệp và lựa chọn không đưa ra mức trừu tượng khác trông không hẳn là thông minh. Nó cũng chẳng đặc biệt tuân theo triết lý Unix
Định dạng định nghĩa unit cũng vậy, nếu hồi đó đã có TOML thì có lẽ họ đã dùng thứ gì hợp lý hơn
Chắc là vì tôi già rồi. Hiển nhiên chỉ có
systemd-*mới là con đường đúng duy nhất, còn ai nghĩ khác thì đều là mấy ông thích chỉ dạy đời mà thôiTôi đã dùng Linux hơn 20 năm, và systemd hơn 10 năm
Thế mà lúc nào cũng vẫn có cái mới để học, và giờ tôi thực sự xem đây như thêm một công cụ hữu ích nữa
Ý kiến trên Lobste.rs
systemd không hoàn hảo, nhưng tôi có cảm giác nhiều quyết định thiết kế của nó dựa trên những bài học rút ra từ cách làm truyền thống trước đây
Gần đây tôi nghe lại tập năm 2015 của CRE, nơi Lennart Poettering giải thích bối cảnh đó, và đến giờ vẫn đáng để giới thiệu
Tôi thuộc phe không thích systemd từ tận xương tủy, nhưng systemd.timers là một trong số ít khái niệm “còn đỡ hơn” của sản phẩm này
Vì vậy tôi khá bất ngờ khi tác giả lại bảo vệ nó theo kiểu hạ thấp những người có phàn nàn chính đáng
Dù vậy, dùng cùng lệnh
atthì khá ổn. Với lệnh chỉ cần chạy một lần vào thời điểm cụ thể thì dùngat, còn lại thì dùng systemd timer và file unit đơn giảnĐiều tôi muốn thấy được cải thiện nhất là có thể biết người dùng nào đang chạy timer. Tôi có lẽ là một trong số ít người vẫn vận hành shellbox vào năm 2026, nhưng sẽ rất hữu ích nếu biết timer nào đang gõ đĩa mỗi giây là do người dùng nào tạo ra
Theo tôi biết thì với
loginctl enable-linger, nó có thể chạy ngay cả khi không có phiên người dùng đang hoạt động. Tất nhiên sẽ có những trường hợp cách đó vẫn chưa đủ, và tôi không biết tình huống cụ thểTôi đặc biệt mong systemd timer có gánh nặng ban đầu thấp hơn ở phía quản lý người dùng
Xét về lượng cấu hình cần thiết thì thực sự rất khó thắng được
crontab -eTôi đã suy nghĩ khá lâu về cách thu thập log cron script một cách có hệ thống, rồi nhận ra là chỉ cần dùng systemd timer
Thế là vấn đề logging được giải quyết. Giờ tôi không còn lý do gì để quay lại cron nữa, giá mà biết điều này sớm hơn
logger, hoặc nối vào file log bằng>>, hoặc cứ để mặc định rồi nhận email là được sao?Cứ bảo là lỗi thời cũng được, nhưng tôi vẫn cấu hình email từ server gửi đến mình
Nếu tự động hóa sẵn thì mỗi host mới đều có kèm theo miễn phí, và bình thường cũng khá tiện
Ví dụ như mở một multiplexer, chạy
long_running_process | mail root@localhost -s "done $?"rồi quên luônBài viết hay, và tôi cũng từng có bản nháp một bài tương tự, đến gần đây lại phải tham khảo lại
Nếu bạn giống tôi và đã chui vào hang thỏ systemd, thì tôi khuyên nên liên kết tượng trưng các file unit và timer trong thư mục project liên quan vào
/etc/systemd/system/Một điều tôi không thích ở systemd là nó không phân biệt unit do distro cài với unit do mình tự viết, và dùng symbolic link thì có thể tự duy trì sự tách biệt đó
Unit hệ thống/gói/phân phối nằm trong
/usr/lib/systemd/system, còn local override hoặc local unit thì nằm trong/etc/systemd/system