Systemd Timer: Pengganti Cron di Linux

Diperbaharui 7 November 2026 8:09 PM

Kalau semua tugas berkala di server Linux masih ditumpuk di satu crontab, ada cara yang lebih rapi di distro modern: systemd timer. Penjadwal bawaan systemd ini menjalankan script lewat unit file, mencatat setiap eksekusi ke journal, dan bisa mengejar jadwal yang terlewat saat server mati. Tidak ada paket tambahan yang perlu dipasang karena semuanya sudah ada di Ubuntu, Debian, Fedora, dan Arch.

Artikel ini memindahkan cron job ke systemd timer langkah demi langkah: anatomi dua file unit, sintaks OnCalendar=, opsi catch-up, sampai cara membaca log ketika timer aktif tetapi script tidak pernah jalan. Contoh mengacu ke systemd 262 dan sudah dicoba di systemd 255 yang banyak dipakai di server.

Kenapa Pindah dari Cron?

Cron hanya tahu satu hal: menjalankan perintah pada jam tertentu. Sisanya diurus manual: log di-redirect, status terakhir tidak bisa ditanya, dan kalau server mati saat jadwal lewat tugas itu hilang tanpa jejak.

systemd timer menutup celah itu: kita tetap menulis “jalankan script ini tiap hari jam 02:30”. Bedanya, eksekusi tercatat di journal, status bisa dicek lewat systemctl status, dan unit bisa diberi batasan seperti Nice= atau ketergantungan pada unit lain, misalnya menunggu jaringan siap sebelum backup.

Kapan cron masih lebih pas

Di container minimal yang tidak menjalankan systemd sebagai init, cron atau supervisor tetap wajar, begitu pula di macOS dan BSD. Kalau kebutuhannya satu baris perintah tanpa log, retry, dan ketergantungan unit, crontab lebih cepat ditulis.

Anatomi: Satu Timer, Satu Service

Systemd memisahkan “apa yang dijalankan” dan “kapan dijalankan” ke dua file. .service mendeskripsikan perintah, .timer mendeskripsikan jadwal. Keduanya memakai nama dasar yang sama: backup-db.timer otomatis mengaktifkan backup-db.service. Kalau namanya berbeda, tunjuk pasangannya lewat direktif Unit=.

Untuk tugas berkala, pakai Type=oneshot: proses dijalankan, selesai, lalu unit dianggap tidak aktif. Jangan tambahkan RemainAfterExit=yes; unit seperti itu tetap dianggap aktif setelah prosesnya keluar, sehingga jadwal berikutnya tidak dieksekusi.

Timer Pertama: Backup Database Tiap Pagi

Simpan file service di /etc/systemd/system/backup-db.service:

[Unit]
Description=Backup database harian
After=network-online.target

[Service]
Type=oneshot
User=backup
Nice=10
ExecStart=/usr/local/bin/backup-db.sh

Lalu timer di direktori yang sama, backup-db.timer:

[Unit]
Description=Jalankan backup database tiap hari jam 02:30

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=15min
AccuracySec=1min

[Install]
WantedBy=timers.target

Bagian [Install] wajib ada di file timer. Tanpa WantedBy=timers.target, unit tidak ikut aktif saat sistem boot.

Mengaktifkan dan memeriksa status

sudo systemctl daemon-reload
sudo systemctl enable --now backup-db.timer
systemctl list-timers --all
sudo systemctl start backup-db.service   # uji script tanpa menunggu jadwal

list-timers menampilkan kolom NEXT, LEFT, LAST, PASSED, UNIT, dan ACTIVATES. Kalau timer tidak muncul atau NEXT kosong, biasanya daemon-reload terlewat atau nama file salah. enable --now menyalakan timer, bukan service-nya.

Monotonic atau Realtime?

Ada dua keluarga timer. Timer monotonic dihitung relatif terhadap suatu titik: OnBootSec= sejak mesin boot, OnStartupSec= sejak service manager start, OnActiveSec= sejak timer aktif, OnUnitActiveSec= sejak service terakhir dijalankan, dan OnUnitInactiveSec= sejak service terakhir berhenti. Timer realtime memakai OnCalendar= dan mengikuti jam dinding.

[Timer]
# 10 menit setelah boot, lalu tiap 6 jam sejak eksekusi terakhir
OnBootSec=10min
OnUnitActiveSec=6h

Kombinasi itu adalah pola klasik tugas berinterval: kita tidak perlu tahu jam berapa server menyala. Satu perilaku yang sering mengejutkan, kalau nilai OnBootSec= sudah lewat saat timer diaktifkan, service langsung dijalankan seketika; direktif monotonic lain tidak begitu. Jam monotonic juga umumnya berhenti saat suspend, sedangkan timer realtime mengejar event yang terlewat begitu mesin bangun.

Sintaks OnCalendar yang Sering Dipakai

Formatnya HariTanggal Tahun-Bulan-Hari Jam:Menit:Detik, dan setiap komponen boleh berisi *, daftar dipisah koma, rentang .., atau pengulangan dengan garis miring. Bentuk yang paling sering muncul:

  • daily, hourly, weekly, monthly, yearly — singkatan yang dinormalisasi otomatis. weekly berarti Senin 00:00, bukan “tujuh hari sejak terakhir”.
  • Mon..Fri *-*-* 08:00:00 — hari kerja saja, jam delapan pagi.
  • Mon,Fri *-*-* 09:30 — dua hari tertentu, detik dianggap :00.
  • *:0/15 — tiap 15 menit, dinormalisasi menjadi *-*-* *:00/15:00.
  • *-*-* 03:00:00 Asia/Jakarta — mengikuti zona tertentu walau server memakai UTC.

Uji dulu dengan systemd-analyze calendar "Mon..Fri *-*-* 08:00:00" --iterations=3; perintah ini menormalkan tulisan kita dan menghitung event berikutnya. Hati-hati menyalin contoh dari dokumentasi terbaru: man page systemd 262 mendokumentasikan tanda ~ untuk hari ke-N dari belakang, tetapi di systemd 255 ekspresi seperti *-*-~01 masih ditolak dengan pesan Invalid argument.

Persistent=true untuk Jadwal yang Terlewat

Opsi kecil ini menyelamatkan banyak backup. Dengan Persistent=true, systemd menyimpan waktu eksekusi terakhir ke disk. Saat timer diaktifkan kembali setelah server mati semalaman, systemd memeriksa apakah ada jadwal yang seharusnya lewat; kalau iya, service dijalankan sekali dan penundaan acak tetap berlaku. Direktif ini hanya berlaku pada timer OnCalendar=. Kalau unit mau dihapus, bersihkan dulu dengan systemctl clean --what=state backup-db.timer.

RandomizedDelaySec vs AccuracySec

Dua opsi ini sering tertukar. AccuracySec= (default satu menit) mengizinkan systemd menggeser eksekusi agar bisa digabung dengan bangunnya CPU lain, tujuannya hemat daya. RandomizedDelaySec= (default nol) menambah penundaan acak supaya banyak mesin tidak menembak server yang sama pada detik identik.

Kalau tujuannya benar-benar menyebar beban, jangan biarkan nilai default AccuracySec menggabungkan semuanya: setel AccuracySec=1us dan beri RandomizedDelaySec= yang cukup besar. Untuk armada server ada dua opsi tambahan: FixedRandomDelay= (sejak systemd 247) membuat penundaan acak yang stabil per mesin, dan RandomizedOffsetSec= (sejak systemd 258) menggeser jadwal tanpa terpengaruh restart sehingga irama mingguan tetap terjaga.

Jebakan yang dijelaskan di man page: penundaan acak lebih panjang dari siklus reboot bisa membuat timer tidak pernah jalan, karena mesin yang sering restart mengulang hitungan dari awal setiap boot.

Debug: Timer Aktif tapi Script Tidak Jalan

Hampir semua penyebabnya bisa ditemukan dalam tiga perintah: cek jadwal, cek hasil eksekusi terakhir, lalu baca log service (bukan log timer).

systemctl list-timers backup-db.timer
systemctl status backup-db.timer backup-db.service
journalctl -u backup-db.service -n 50 --no-pager
systemd-analyze verify /etc/systemd/system/backup-db.timer

Penyebab tersering: lupa daemon-reload setelah mengedit unit, nama file timer dan service tidak cocok, script tidak punya bit executable, atau ExecStart= memakai path relatif. systemd tidak mewarisi PATH shell interaktif, jadi tulis path absolut seperti /usr/local/bin/backup-db.sh. Kalau script jalan tetapi gagal di tengah, exit code dan stderr-nya muncul di journal. systemd-analyze verify tidak mengeksekusi apa pun, jadi aman di server produksi.

Timer User Tanpa Sudo

Tidak semua tugas perlu root. Script pribadi bisa dijalankan sebagai user biasa dengan unit di ~/.config/systemd/user/, perintah systemctl memakai flag --user, dan log dibaca lewat journalctl --user.

mkdir -p ~/.config/systemd/user
systemctl --user daemon-reload
systemctl --user enable --now sync-notes.timer
journalctl --user -u sync-notes.service -n 30
sudo loginctl enable-linger $USER   # tetap jalan walau tidak login

Baris terakhir sering terlewat. Tanpa lingering, service manager milik user dimatikan saat sesi terakhir logout sehingga timer ikut berhenti.

FAQ

Apa beda systemctl start pada timer dan pada service?

Menjalankan start pada unit .timer hanya mengaktifkan penjadwalnya; tidak ada script yang dieksekusi saat itu juga. Untuk menjalankan tugasnya segera, panggil unit .service-nya.

Kalau server mati ketika jadwal lewat, apakah tugas tetap dijalankan?

Hanya jika timer memakai OnCalendar= dan Persistent=true. Systemd menjalankannya sekali saat sistem kembali aktif, bukan mengulang setiap jadwal yang terlewat. Timer monotonic tidak punya perilaku catch-up karena tidak punya acuan jam dinding.

Bisakah timer menjalankan docker compose?

Bisa. Tulis perintah lengkap di ExecStart= dengan path absolut, misalnya ExecStart=/usr/bin/docker compose -f /srv/app/docker-compose.yml up -d. Kalau butuh pipe atau variabel shell, bungkus dalam script lalu panggil script itu.

Kesimpulan

Memindahkan cron job ke systemd timer bukan sekadar mengganti sintaks jadwal. Kita mendapat log terpusat di journal, status unit yang bisa ditanya kapan saja, batasan resource, dan opsi catch-up yang membuat tugas penting tidak hilang saat server mati. Polanya konsisten: satu .service untuk perintah, satu .timer untuk jadwal, lalu enable --now pada unit timer.

Mulai dari satu tugas terpenting, biasanya backup. Verifikasi ekspresi kalender, pasang Persistent=true, lalu bandingkan hasilnya di journalctl beberapa hari sebelum memindahkan cron job lain. Kalau butuh fondasi server Linux, artikel cara install LAMP dan export database lewat command line bisa jadi latihan sebelum timer backup dipasang. Untuk memeriksa hasil backup manual, panduan phpMyAdmin di Linux juga masih relevan.

Referensi

susantokun avatar
susantokun
Hanya seorang programmer yang fokus di bidang web development. Tidak nyaman dengan keramaian dan suka akan keindahan.
Kebijakan Berkomentar :
1. Dilarang berkomentar yang mengandung SPAM, SARA, HOAX, PORNO.2. Mohon sertakan informasi detail saat terjadi error (pesan error, sreenshoot, code, logs, dsb.).