بازگشت به وبلاگ

cron در مقابل timer های systemd: کدام را به‌کار ببرید؟

۲۰ شهریور ۱۴۰۵

روی توزیع امروزی، برای هر job ای که خودتان صاحبش هستید timer مربوط به systemd را به‌کار ببرید و cron را نگه دارید برای job های یک‌خطی کاربر و سرورهایی که خودتان راه نینداخته‌اید. Timer ها اجرای هر بار را در journal لاگ می‌کنند، می‌توانند برنامه‌های زمانیِ از دست رفته در وقتی که ماشین خاموش بوده را جبران کنند، و به همان فایل‌های unit متکی‌اند که بقیه سیستم. cron در کوتاهی می‌برد — یک خط crontab -e از دو فایل unit جلوتر است — و هنوز تنها زمان‌بندی‌ای است که وجودش روی کانتینرهای مینیمال و یونیکس‌های عجیب تضمین شده. اما یک نکته: cron بی‌سروصدا شکست می‌خورد. اگر job تان ساعت 3 نصف‌شب خطا بدهد، cron برای یک mailbox محلی ایمیل می‌فرستد که هیچ‌کس نمی‌خواند، در حالی که timer با journalctl -u mytimer.service کل خروجی را جلوی شما می‌گذارد. در ادامه: تفاوت‌های واقعی، روش تست هر کدام، چرایی اجرا نشدن timer، و یک جدول تصمیم.

تفاوت cron و timer مربوط به systemd چیست؟

cron یک daemon است که جدولی از خط‌ها را می‌خواند — پنج فیلد زمانی و یک دستور — و هر دستور را وقتی ساعت سیستم با آن match شد اجرا می‌کند. کل مدل همین است. از job به‌مثابه یک شیء هیچ تصوری ندارد: نه unit دارد، نه وضعیت، نه وابستگی، نه لاگ مخصوص خودش.

timer مربوط به systemd یک فایل unit است (foo.timer) که وقتی برنامه‌اش برسد فایل unit دیگری را (foo.service) شلیک می‌کند. job اینجا یک شیء درجه‌یک است با start و status و logs و ردیابی شکست، مثل هر سرویس دیگری. زمان‌بندی هم یا بر پایه تقویم است (شبیه cron) یا monotonic (OnBootSec=15min، که تغییرات ساعت دیواری نمی‌تواند گیجش کند).

پیامدهای عملی:

  • لاگ: timer ها خروجی stdout/stderr را به ازای هر unit در journal می‌نویسند؛ cron در بهترین حالت برای کاربر محلی میل می‌فرستد.
  • اجراهای از دست رفته: timer با Persistent=true اگر برنامه‌اش را از دست داده باشد یک بار روی boot اجرایش می‌کند؛ cron صرفاً رد می‌شود.
  • وابستگی‌ها: timer ها می‌توانند منتظر network-online.target یا نقطه‌های mount بمانند؛ cron منطق retry را باید دستی داخل اسکریپت بنویسید.
  • سینتکس: cron یک خط است؛ timer دو فایل. کل هزینه جابه‌جایی همین است.

کدام سینتکس زمان‌بندی ساده‌تر است — crontab یا OnCalendar؟

پنج فیلد cron، حاکم فشرده میدان است: */15 * * * * یعنی هر 15 دقیقه و بیشتر ادمین‌ها آن را در خواب هم می‌خوانند. OnCalendar= در systemd پرگواترتر است ولی به‌طور قطعی رساتر، و systemd-analyze calendar پیش از اینکه آن را ثبت کنید اجراهای بعدی را به شما می‌گوید — cron هیچ معادل dry-run ای ندارد.

# Cron: every day at 03:30
30 3 * * * /usr/local/bin/backup.sh

# systemd: the same schedule, verifiable before you save it
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 تا جلوی هجوم هم‌زمان هزار ماشین به یک سرور گرفته شود.

چطور بدون انتظار یک timer مربوط به systemd را تست کنم؟

سه دستور به همه چیز جواب می‌دهند. برنامه‌های در صف و اجرای بعدی‌شان را فهرست کنید، job را دقیقاً مثل timer به‌صورت دستی اجرا کنید، بعد لاگ‌هایش را بخوانید:

systemctl list-timers --all                 # every timer, next + last run
sudo systemctl start backup.service         # fire the job now, same unit as the timer
journalctl -u backup.service -f             # watch its output live

نکته، تقسیم کار است: systemctl start backup.timer برنامه را مسلح می‌کند؛ خود job همان service است. اگر list-timers تایمرتان را نشان می‌دهد، systemctl status backup.service سبز است و journal خروجی‌تان را دارد، کل زنجیره کار می‌کند.

چطور یک cron job را دستی اجرا کنم؟

cron job ها با یک environment لخت اجرا می‌شوند — به همین دلیل «در shell خودم کار می‌کند، در cron نه» یک ژانر باگ است. برای بازسازی وفادار cron، دستور را با همان environment ای که cron استفاده می‌کند از طریق sh اجرا کنید:

crontab -l                                  # confirm the exact line
env -i SHELL=/bin/sh PATH=/usr/bin:/bin sh -c '/usr/local/bin/backup.sh'
grep CRON /var/log/syslog | tail            # did cron even fire it? (Debian/Ubuntu)
journalctl -u cron -n 20                    # same, on systemd distros

همان خط env -i آزمون دستیِ صادقانه است: یک environment عریان با PATH پیش‌فرض cron. بیشتر شکست‌های بی‌سروصدای cron یا PATH لخت است یا یک % بی‌کوت در دستور (cron با % مثل newline رفتار می‌کند)، و هر دو همین‌جا بلافاصله خودشان را نشان می‌دهند.

چرا timer مربوط به systemd من شلیک نمی‌شود؟

چهار علت تقریباً همه موارد را پوشش می‌دهد، به ترتیبی که باید بررسی شوند:

  1. نام سرویس و 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. بدون این، برنامه جدیدتان live نیست.
  3. Persistent=true بدون سمانتیک OnCalendar که انتظارش را دارید — یا دارید به‌جای list-timers به systemctl status foo.timer نگاه می‌کنید (همیشه active نشان می‌دهد، حتی وقتی بیکار است).
  4. سرویسی که شلیک می‌کند بلافاصله می‌میرد، برای همین timer مرده به نظر می‌رسد. journalctl -u foo.service --since -1h همان crash ای را نشان می‌دهد که list-timers پنهان می‌کند.

و وقتی سراغ لاگ‌ها رفتید، چیت‌شیت journalctl فیلترهای (-u، --since، -f) را پوشش می‌دهد که این کار را سریع می‌کنند.

cron در مقابل timer های systemd: جدول تصمیم

crontimer مربوط به systemd
راه‌اندازییک خط crontabدو فایل unit
لاگمیل محلی، معمولاً نخواندهjournal، به ازای هر unit
اجرای از دست رفته (ماشین خاموش)رد می‌شودبا Persistent=true یک بار اجرا می‌شود
وابستگی / ترتیبهیچ — DIY داخل اسکریپتوابستگی‌های کامل unit
تأخیر تصادفیDIY با $RANDOMRandomizedDelaySec=
تست/dry-run برنامهنداردsystemd-analyze calendar
همه‌جا موجود استبله — کانتینرها، BSDها، embeddedsystemd لازم دارد (PID 1)
job کاربری بدون rootcrontab -eunit های systemd --user

قاعده سرانگشتی: job ای روی ماشین systemd خودتان → timer. یادآور شخصی سریع یا ماشینی که خودتان نساخته‌اید → cron. هر چیزی با وابستگی، retry، یا نیاز به اینکه بدانید واقعاً اجرا شده یا نه → timer، همیشه. الگوی رایج، timer ای است که یک job نگهداری را اجرا می‌کند — مثلاً بکاپ rsync شبانه — جایی که Persistent=true تضمین می‌کند بکاپ حتی اگر ماشین سر دقیقه مقرر خواب بوده انجام شود؛ چیزی که cron به‌سادگی نمی‌دهد.

FAQ

آیا cron job ها روی Linux منسوخ شده‌اند؟

نه — cron برای تریگرهای زمانی ساده سرِ جایش است. Timer ها آنجا می‌برند که جبران اجرای از دست رفته، وابستگی یا لاگ در journal می‌خواهید.

systemd timer چه می‌کند که cron از عهده‌اش برنمی‌آید؟

Timer های Persistent اجراهای از دست رفته را بعد از downtime دوباره اجرا می‌کنند؛ cron بی‌سروصدا از آن‌ها می‌گذرد. Timer ها وابستگی‌ها را زنجیر می‌کنند و می‌توانند پنجره شروع را تصادفی کنند.

چطور timer ای را که اجرا نشده دیباگ کنم؟

systemctl list-timers --all آخرین و بعدی اجراها را نشان می‌دهد؛ journalctl -u روی خود timer و unit سرویسش می‌گوید چرا شلیک شده یا شکست خورده.

— mrsaynothing

— mrsaynothing

یادداشت‌های میدانی درباره AI، لینوکس و self-hosting.

این نوشته را در dev.to بحث کنید dev.to ↗

آموزش بعدی با ایمیل

هر نوشته یک ایمیل. درستش کن، برو سراغ بعدی.

self-hosted · بدون واسطه‌های ثالث · لغو اشتراک با یک کلیک

این چیست؟

llama.cpp یا Ollama: در 2026 کدام را اجرا کنید؟

این نوشته‌ها را می‌پسندید؟ این همان کاری است که برای زندگی از آن درمی‌آورم. استخدامم کنید