Bali, Indonesia
Insights

Service Kena start-limit-hit, Naikkan StartLimitBurst atau Cari Penyebab Crash?

October 31, 2026
0:00
Service yang biasanya bangkit sendiri setelah crash tiba-tiba diam di status failed, dan 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.
Gejalanya biasanya sederhana. Website tidak bisa diakses, worker queue berhenti memproses job, atau API internal menolak koneksi. Saat dicek, 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.
Langkah pertama selalu read-only. Kita perlu tahu dua hal: kenapa proses keluar, dan berapa kali itu terjadi.
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 100
systemctl 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.
Baris-baris ini menjelaskan kronologi, bukan penyebab. Penyebabnya ada di atasnya, yaitu output stderr aplikasi sebelum 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/FAILURE berarti aplikasi keluar sendiri dengan error. Cari pesan error aplikasi di baris sebelumnya.
  • code=killed, status=9/KILL berarti proses dibunuh dari luar. Salah satu kemungkinan yang sering muncul adalah OOM Killer, jadi cek juga log kernel dengan journalctl -k -b.
  • status=203/EXEC berarti systemd gagal menjalankan binary sama sekali, misalnya path di ExecStart= salah atau file tidak executable.
Di titik ini biasanya terlihat jelas bahwa service crash karena hal yang tidak akan sembuh dengan dicoba ulang. Penyebab yang sering muncul antara lain:
  • 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.
Coba pikirkan apa yang terjadi kalau 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.
Urutan yang aman saat service produksi sedang mati:
  1. Perbaiki penyebab yang ditemukan di journal. Kalau aplikasinya punya mode validasi config, jalankan dulu sebelum start ulang.
  2. Jalankan systemctl reset-failed nama-service untuk menghapus state failed dan counter rate limit unit tersebut.
  3. Jalankan systemctl start nama-service, lalu pantau dengan journalctl -u nama-service -f selama beberapa saat.
Langkah kedua penting. Start manual lewat 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.
Setelah penyebab crash beres, baru masuk akal untuk menilai ulang restart policy. Tujuannya dua: crash sesaat bisa pulih sendiri, dan crash permanen tetap berhenti dan terlihat. Ubah lewat drop-in, jangan mengedit unit file bawaan paket, supaya tidak tertimpa saat update. 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=5s
Cara membacanya: dengan jeda 5 detik, lima percobaan butuh sekitar 20 sampai 25 detik. Itu masih di dalam jendela 300 detik, jadi crash loop permanen tetap berhenti dan unit masuk failed. Sementara crash tunggal yang jarang terjadi akan pulih dengan sendirinya. Perhatikan hubungan ketiga nilai itu. Kalau RestartSec 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=always juga me-restart service yang keluar dengan normal. Biasanya on-failure lebih 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= dan Wants= 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 begitu start-limit-hit langsung sampai ke orang yang perlu tahu.
  • Di systemd versi baru ada RestartSteps= dan RestartMaxDelaySec= untuk jeda restart yang bertambah bertahap. Cek man systemd.service di 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 dengan systemctl show nama-service -p StartLimitBurst.
Dari sisi support, pertanyaan pertama yang paling berguna untuk pelanggan bukan berapa batas restart-nya, melainkan apa yang berubah tepat sebelum service mulai gagal: update paket, edit config, migrasi, atau lonjakan traffic. Jawaban itu biasanya mempersempit isi journal yang perlu dibaca. Jadi saat melihat 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.