systemctl status menulis start-limit-hit. Refleks pertama banyak orang adalah menaikkan StartLimitBurst atau mematikan batasnya, supaya systemd terus mencoba. Masalahnya, batas itu bukan penyebab service mati. Batas itu hanya alasan kenapa systemd berhenti mencoba.
Tulisan ini disusun seperti post-mortem. Ini bukan insiden nyata tertentu, melainkan pola yang sering muncul di VPS dan server dedicated: dari dampak awal, jalur investigasi, titik temu penyebabnya, mitigasi sementara, sampai perbaikan konfigurasi yang lebih masuk akal.
Dampak awal: service mati dan tidak mau bangkit lagi
systemctl status nama-service menunjukkan Active: failed dengan result start-limit-hit.
Yang sering membingungkan pelanggan: service ini sudah memakai Restart=on-failure, jadi seharusnya restart otomatis, kan? Betul, dan memang sudah. Systemd sudah mencoba beberapa kali dalam waktu singkat. Karena setiap percobaan gagal lagi, systemd menyerah sesuai aturan rate limit-nya.
Aturan itu diatur dua nilai. StartLimitIntervalSec= adalah jendela waktunya. StartLimitBurst= adalah jumlah start maksimal di dalam jendela itu. Default umumnya 5 kali start dalam 10 detik, diambil dari DefaultStartLimitIntervalSec dan DefaultStartLimitBurst di /etc/systemd/system.conf. Kalau batas terlampaui, unit masuk state failed dan tidak dicoba lagi sampai ada intervensi.
Ada satu detail yang sering terlewat. Default RestartSec=, yaitu jeda sebelum restart, sangat pendek: 100 milidetik. Jadi service yang crash langsung saat start bisa menghabiskan jatah 5 percobaan dalam sekitar satu detik. Dari luar terlihat seperti service mati sekali lalu diam. Padahal di belakang sudah terjadi crash loop singkat.
Jalur investigasi: dari systemctl status ke journalctl -u
BASH
# Ringkasan state, result terakhir, dan potongan log terbaru
systemctl status nama-service
# Result, jumlah restart, dan exit status proses utama
systemctl show nama-service -p Result -p NRestarts -p ExecMainStatus
# Log unit ini sejak boot terakhir, 100 baris terakhir
journalctl -u nama-service -b --no-pager -n 100systemctl status hanya menampilkan beberapa baris terakhir, dan baris terakhir hampir pasti pesan dari systemd sendiri, bukan dari aplikasi. Karena itu journalctl -u lebih penting. Polanya kira-kira seperti ini (contoh format output, teks persisnya bisa berbeda antar versi):
TEXT
nama-service.service: Main process exited, code=exited, status=1/FAILURE
nama-service.service: Failed with result 'exit-code'.
nama-service.service: Scheduled restart job, restart counter is at 5.
nama-service.service: Start request repeated too quickly.
nama-service.service: Failed with result 'start-limit-hit'.
Failed to start nama-service.service - Deskripsi Service.Main process exited yang pertama. Scroll ke atas sampai ketemu crash paling awal. Kalau log terlalu banyak, persempit dengan --since ke sekitar waktu service mulai bermasalah.
Bagian code= dan status= juga memberi petunjuk arah:
code=exited, status=1/FAILUREberarti aplikasi keluar sendiri dengan error. Cari pesan error aplikasi di baris sebelumnya.code=killed, status=9/KILLberarti proses dibunuh dari luar. Salah satu kemungkinan yang sering muncul adalah OOM Killer, jadi cek juga log kernel denganjournalctl -k -b.status=203/EXECberarti systemd gagal menjalankan binary sama sekali, misalnya path diExecStart=salah atau file tidak executable.
Titik temu: start-limit-hit itu rem, bukan penyakitnya
- Typo di file config aplikasi.
- Port sudah dipakai proses lain (
Address already in use). - Permission direktori data atau socket berubah setelah migrasi.
- Environment file yang dirujuk
EnvironmentFile=hilang. - Database belum siap saat service start di waktu boot.
StartLimitBurst dinaikkan ke angka besar atau StartLimitIntervalSec=0 dipasang untuk mematikan rate limit. Kalau penyebabnya config rusak, service tetap tidak akan hidup. Bedanya, sekarang systemd mengulangnya terus. Journal dibanjiri pesan yang sama, CPU terpakai untuk start-crash-start, dan yang paling merugikan: state failed hilang dari radar. Monitoring yang mengandalkan systemctl --failed tidak melihat apa-apa, karena unit selalu sedang dalam proses restart.
Jadi start-limit-hit justru informasi berguna. Systemd sedang bilang bahwa masalahnya bukan gangguan sesaat.
Kalau penyebabnya ternyata OOM, perbaikannya ada di sisi alokasi memori, bukan di restart policy. Untuk lingkungan multi-tenant, pembahasan isolasi memori dengan cgroups v2 dan systemd slice lebih relevan daripada menaikkan batas restart.
Mitigasi sementara yang tidak menutupi masalah
- Perbaiki penyebab yang ditemukan di journal. Kalau aplikasinya punya mode validasi config, jalankan dulu sebelum start ulang.
- Jalankan
systemctl reset-failed nama-serviceuntuk menghapus state failed dan counter rate limit unit tersebut. - Jalankan
systemctl start nama-service, lalu pantau denganjournalctl -u nama-service -fselama beberapa saat.
systemctl start juga dihitung oleh rate limit. Kalau kamu mencoba start berkali-kali saat penyebabnya belum beres, kamu sendiri yang menghabiskan jatahnya. Hasilnya, unit kena start-limit-hit lagi dan kesannya systemd yang bermasalah.
Kalau penyebabnya belum ketemu tapi layanan harus hidup, menjalankan binary secara manual di foreground dengan user dan environment yang sama biasanya memperlihatkan error lebih jelas. Caranya tergantung aplikasi dan unit file masing-masing, jadi lihat ExecStart=, User=, dan EnvironmentFile= di unit tersebut sebagai acuan.
Perbaikan permanen: menyetel Restart= dan batasnya secara sadar
systemctl edit nama-service membuat file override dan otomatis menjalankan daemon-reload setelah disimpan. Contoh isinya (nilai ilustratif, sesuaikan dengan karakter service):
INI
[Unit]
# Maksimal 5 kali start dalam jendela 300 detik
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
# Restart hanya saat gagal, bukan saat dihentikan normal
Restart=on-failure
# Beri jeda agar dependensi sempat pulih
RestartSec=5sRestartSec dikali StartLimitBurst lebih besar dari StartLimitIntervalSec, batasnya praktis tidak pernah tercapai dan service akan dicoba selamanya. Kadang itu memang diinginkan, misalnya untuk agent yang harus terus mencoba konek ke server pusat. Tapi pilihan itu sebaiknya diambil dengan sadar, bukan efek samping yang tidak disengaja.
Beberapa pertimbangan tambahan:
Restart=alwaysjuga me-restart service yang keluar dengan normal. Biasanyaon-failurelebih tepat untuk daemon biasa.- Kalau aplikasi memakai exit code tertentu untuk menandai error config,
RestartPreventExitStatus=bisa mencegah restart sia-sia untuk kode tersebut. - Kalau crash terjadi karena service start sebelum dependensinya siap, perbaikan yang lebih tepat adalah urutan
After=danWants=yang benar, atau logika retry di aplikasi itu sendiri, bukan burst yang lebih besar. OnFailure=di bagian[Unit]bisa memicu unit lain, misalnya pengirim notifikasi, saat service masuk failed. Dengan begitustart-limit-hitlangsung sampai ke orang yang perlu tahu.- Di systemd versi baru ada
RestartSteps=danRestartMaxDelaySec=untuk jeda restart yang bertambah bertahap. Cekman systemd.servicedi server sebelum memakainya, karena versi lama tidak mengenalnya. - Di systemd lama, nama directive-nya
StartLimitInterval=dan letaknya di[Service]. Kalau setelan di[Unit]terlihat diabaikan, cek versi dan nilai efektifnya dengansystemctl show nama-service -p StartLimitBurst.
start-limit-hit, anggap itu tanda untuk berhenti mencoba dan mulai membaca. Cari crash pertama di journalctl -u, perbaiki penyebabnya, reset-failed, baru start ulang. Setelah itu, setel RestartSec dan StartLimitIntervalSec supaya gangguan sesaat bisa pulih sendiri, sementara kegagalan permanen tetap berhenti dan terlihat di monitoring.