Kebijakan Restart systemd untuk Home Server - Pulihkan Service Tanpa Menyembunyikan Kegagalan
Sebuah service yang berjalan terus-menerus di home server berhenti pada pukul tiga pagi. Haruskah mesin membiarkannya mati, langsung menjalankannya kembali, atau terus mencoba sampai pagi? Pemulihan otomatis terdengar seperti jawaban yang jelas, tetapi restart loop tanpa batas dapat mengubah satu kegagalan yang jelas menjadi noise, beban berulang, dan diagnosis yang lebih sulit.
systemd menyediakan beberapa kontrol untuk masalah ini. Pertanyaan yang berguna bukan sekadar "bagaimana agar sebuah service melakukan restart?" Pertanyaannya adalah: kegagalan mana yang perlu memicu percobaan baru, seberapa cepat, dan kapan percobaan harus berhenti? Artikel ini menyusun kebijakan konservatif untuk service generik yang berjalan lama di Debian. Tidak ada asumsi bahwa satu kumpulan nilai cocok untuk setiap workload.
Pemulihan tidak sama dengan perbaikan
Restart dapat membersihkan state sementara: sebuah process mungkin menemukan network error sesaat, kehabisan resource yang dilepaskan ketika process berhenti, atau crash karena defect yang terisolasi. Menjalankan process baru mungkin memulihkan service. Namun, restart juga dapat mengulangi error permanen, seperti konfigurasi tidak valid, credential yang hilang, database schema yang tidak kompatibel, atau disk penuh.
Perbedaan ini penting karena systemd mengawasi sebuah process; systemd tidak memahami apakah respons aplikasi sudah benar. Sebuah process bisa tetap berjalan sambil mengembalikan error, menunggu dependency tanpa akhir, atau menyajikan data lama. Sebaliknya, process yang sengaja berhenti setelah menyelesaikan tugasnya mungkin sehat meskipun tidak lagi berjalan.
Anggap restart otomatis sebagai salah satu lapisan dalam sistem operasional, bukan bukti kesehatan. Log, pemeriksaan eksternal, pemantauan resource, dan readiness yang khusus untuk aplikasi tetap memiliki peran masing-masing.
Pilih kondisi berhenti yang layak dicoba kembali
Pengaturan utama berada di bagian [Service] milik unit. Menurut manual systemd.service Debian Trixie, Restart=on-failure mencoba kembali setelah kejadian seperti exit code non-zero, signal yang tidak bersih, timeout, atau kegagalan watchdog. Secara normal, pilihan ini tidak melakukan restart setelah clean exit atau perintah systemctl stop yang eksplisit.
[Service]
Restart=on-failure
RestartSec=10s
Untuk server process yang berjalan lama, on-failure sering menjadi titik awal yang masuk akal karena membedakan error dari tindakan berhenti yang disengaja dan bersih. Manual upstream merekomendasikannya untuk service yang berjalan lama, tetapi itu adalah panduan, bukan aturan universal. Sebuah aplikasi mungkin memakai non-zero code untuk meminta campur tangan operator, atau tanpa sengaja mengembalikan zero setelah kondisi internal yang fatal. Kontrak exit milik service tersebut tetap penting.
Restart=always memiliki cakupan lebih luas: service juga dimulai kembali setelah clean exit. Pilihan ini dapat sesuai untuk process yang harus selalu hadir dan tidak memiliki state "selesai dengan sukses" yang bermakna. Namun, pilihan ini tidak otomatis lebih andal. Jika administrator atau deployment script mengharapkan program berhenti dengan bersih lalu tetap mati, always justru melawan maksud tersebut. Ada pula mode yang lebih sempit seperti on-abnormal dan on-watchdog; pilih hanya ketika kategori exit yang didokumentasikan sesuai dengan aplikasi.
Exit code dapat mempertajam kebijakan
systemd juga menyediakan SuccessExitStatus=, RestartPreventExitStatus=, dan RestartForceExitStatus=. Pengaturan ini dapat mengekspresikan semantik exit yang didokumentasikan oleh aplikasi. Sebagai contoh, code tertentu dapat berarti "konfigurasi tidak valid; jangan coba lagi." Namun, menambahkan aturan seperti itu tanpa kontrak dari aplikasi berisiko. Angka yang mudah diingat bukan pengganti dokumentasi.
Batasi waktu sekaligus jumlah percobaan
RestartSec=10s menambahkan jeda sebelum restart otomatis. Jeda eksplisit mencegah process yang gagal dijalankan kembali hampir seketika. Sepuluh detik hanya contoh: DNS helper lokal, service Java yang besar, dan worker yang memanggil remote API dengan rate limit memiliki biaya pemulihan yang berbeda.
systemd 254 memperkenalkan RestartSteps= dan RestartMaxDelaySec=, yang dapat menaikkan jeda dalam beberapa percobaan restart. Host Debian yang diperiksa menjalankan systemd 257.13, sehingga directive tersebut tersedia di sana. Pembaca perlu memeriksa release pada mesin masing-masing dengan:
systemctl --version
Jeda yang bertambah tetapi tetap berbatas dapat ditulis sebagai berikut:
[Service]
Restart=on-failure
RestartSec=10s
RestartSteps=4
RestartMaxDelaySec=2min
Urutan persisnya dihitung oleh systemd di antara jeda awal dan maksimum; poin operasional yang penting adalah percobaan makin jarang dan kenaikannya berhenti pada batas maksimum yang dikonfigurasi. Pada release sebelum 254, hilangkan dua directive baru tersebut dan gunakan RestartSec= yang tetap. Jangan menempelkan directive yang tidak dikenal lalu berasumsi bahwa systemd menerimanya: systemd dapat mencatat warning dan mengabaikan pengaturan yang tidak dikenal.
Jeda saja tidak membatasi jumlah start. Tugas itu ditangani oleh StartLimitIntervalSec= dan StartLimitBurst= di bagian [Unit]:
[Unit]
StartLimitIntervalSec=10min
StartLimitBurst=5
Manual systemd.unit mendefinisikannya sebagai start-rate limit: start yang melebihi jumlah yang diizinkan dalam interval akan ditolak. Batas ini berlaku untuk semua start, termasuk yang dilakukan manual, bukan hanya yang disebabkan oleh Restart=. Pemeriksaan kondisi unit yang gagal tidak dihitung ke dalam batas.
Ketika service yang terus mencoba mencapai batas tersebut, percobaan otomatis berhenti. Ini bukan circuit breaker permanen. Setelah interval berlalu, aktivasi manual, timer, atau socket berikutnya dapat menjalankan unit dan mengaktifkan kembali perilaku restart. Nuansa ini penting jika kebijakan yang diinginkan adalah "tetap mati sampai manusia menyetujui pemulihan"; start-rate limiting saja tidak menyediakan workflow tersebut.
Gunakan drop-in, bukan mengedit unit dari package
Jika sebuah package memiliki main unit file di bawah /usr/lib/systemd/system/, mengedit file itu secara langsung membuat kebijakan lokal mudah hilang atau membingungkan saat upgrade. systemd mendukung file drop-in yang digabungkan setelah main unit. Manual systemctl mendokumentasikan systemctl edit sebagai interface untuk membuat override.
sudo systemctl edit example-worker.service
Untuk worker generik yang terus berjalan, drop-in yang dihasilkan dapat berisi:
[Unit]
StartLimitIntervalSec=10min
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=10s
RestartSteps=4
RestartMaxDelaySec=2min
example-worker.service adalah placeholder, bukan service yang diamati di server ini. Ganti dengan nama unit yang sebenarnya dan tinjau dokumentasi aplikasi tersebut sebelum memilih nilai. Periksa pula pengaturan yang sudah ada terlebih dahulu:
systemctl cat example-worker.service
systemctl show example-worker.service \
--property=Restart,RestartUSec,NRestarts,StartLimitIntervalUSec,StartLimitBurst
systemctl cat menampilkan main file dan drop-in yang dimuat dari disk. systemctl show menyajikan property yang dinormalisasi serta runtime state; property waktu biasanya memakai akhiran USec meskipun directive di unit file berakhir dengan Sec.
Verifikasi sebelum mengubah service yang berjalan
Konfigurasi yang tampak rapi masih mungkin memiliki directive yang salah eja, bagian yang keliru, atau command yang tidak valid. Manual systemd-analyze menyatakan bahwa verify dapat mendeteksi directive yang tidak dikenal, dependency wajib yang hilang, manual page yang dirujuk tetapi tidak ada, dan command yang tidak tersedia atau tidak executable.
systemd-analyze verify /path/to/example-worker.service
Contoh generik lengkap yang digunakan untuk artikel ini diperiksa dengan systemd 257.13 menggunakan executable yang tersedia. Pemeriksaan tersebut memvalidasi syntax unit pada host itu; pemeriksaan tidak membuktikan bahwa aplikasi nyata akan berjalan, menjadi ready, memproses request dengan benar, atau pulih dengan aman.
Setelah meninjau dan menyimpan drop-in yang sebenarnya, systemctl edit memuat ulang konfigurasi manager ketika editor berhasil ditutup. Sebelum restart, periksa kembali hasil gabungannya. Kemudian jadwalkan restart pada waktu ketika interupsi dapat diterima:
sudo systemctl restart example-worker.service
systemctl status example-worker.service
journalctl --unit=example-worker.service --since today
Restart sendiri merupakan stop yang diikuti start, sehingga dapat menginterupsi pekerjaan aktif. Untuk database, queue worker, atau upload processor, perilaku shutdown dan resume milik aplikasi lebih penting daripada kerapian unit file.
Saat pemulihan berhenti, pertahankan buktinya
Jika unit memasuki state failed atau melaporkan bahwa start limit tercapai, mulailah dari status dan journal, bukan langsung menghapus state:
systemctl status example-worker.service
journalctl --unit=example-worker.service --since today
systemctl status adalah tampilan yang mudah dibaca manusia untuk runtime state saat ini atau terbaru beserta sedikit cuplikan log. Ini bukan riwayat lengkap. Journal dapat menunjukkan percobaan yang lebih lama, selama record tersebut masih disimpan.
Setelah masalah dasarnya ditemukan dan diperbaiki, command berikut menghapus failed state serta counter start dan restart per unit:
sudo systemctl reset-failed example-worker.service
sudo systemctl start example-worker.service
reset-failed tidak memperbaiki konfigurasi, memulihkan credential, mengosongkan disk, atau memperbaiki data aplikasi. Command ini hanya menghapus state dan counter yang dicatat systemd. Menghapusnya sebelum mengumpulkan bukti dapat membuat urutan kejadian lebih sulit dipahami.
Hal yang tidak dapat dijamin oleh restart otomatis
Process supervisor melihat state process dan signal yang diekspos aplikasi. Dengan sendirinya, supervisor tidak dapat menjawab beberapa pertanyaan yang lebih besar:
- Apakah service mengembalikan respons yang benar, bukan sekadar menerima koneksi?
- Apakah database atau remote API yang diperlukan sudah siap?
- Apakah process sebelumnya meninggalkan pekerjaan yang baru selesai sebagian?
- Apakah start berulang akan menambah beban pada dependency yang sedang gagal?
- Apakah seseorang mendapat peringatan setelah pemulihan otomatis berhenti?
Inilah alasan untuk memasangkan kebijakan restart dengan observability dan desain aplikasi. Health check dapat melihat dari sisi pengguna. Structured log dapat mempertahankan penyebab kegagalan. Penanganan job yang idempotent dapat mengurangi kerusakan akibat pekerjaan yang terputus. Alert dapat membuat kebijakan retry yang berbatas menjadi sesuatu yang dapat ditindaklanjuti, bukan kegagalan diam-diam.
Ada pula kontra-argumen terhadap pemulihan agresif: untuk service home server yang tidak esensial, tetap mati mungkin lebih aman daripada berulang kali menyentuh storage, remote API, atau state yang rusak. Availability bukan satu-satunya tujuan. Penggunaan resource, integritas data, dan kemudahan diagnosis patut mendapat bobot yang eksplisit.
Checklist peninjauan praktis
- Pastikan unit mewakili process yang berjalan lama, bukan one-shot task yang sukses.
- Baca dokumentasi exit code dan shutdown milik aplikasi.
- Pilih
on-failure,always, atau mode yang lebih sempit secara sengaja. - Tetapkan jeda restart eksplisit yang sesuai dengan service dan dependency-nya.
- Tambahkan start-rate limit dengan memahami bahwa start manual juga dihitung.
- Periksa versi systemd yang terpasang sebelum memakai directive yang lebih baru.
- Gunakan drop-in, periksa unit hasil penggabungan, dan jalankan
systemd-analyze verify. - Tentukan bagaimana recovery loop yang berhenti menjadi terlihat oleh manusia.
- Lakukan pengujian hanya pada waktu yang aman dengan service yang perilaku kegagalannya sudah dipahami.
Kesimpulan
Kebijakan restart yang berguna bukanlah kebijakan yang terus mencoba selamanya. Kebijakan yang berguna mengklasifikasikan kegagalan yang diperkirakan, memberi jarak yang cukup antarpercobaan, berhenti sebelum sebuah loop menjadi insiden tersendiri, dan mempertahankan jejak untuk diagnosis.
Untuk banyak service yang berjalan lama, Restart=on-failure, RestartSec= yang eksplisit, dan start-rate limit yang dipertimbangkan membentuk baseline yang dapat dipertanggungjawabkan. Namun, semua itu tetap hanya baseline. Nilai yang tepat bergantung pada apa yang dilakukan aplikasi saat mulai, apa yang dapat rusak ketika aplikasi terinterupsi, dan bagaimana operator mengetahui bahwa pemulihan tidak berhasil.
