Artikel pagi sudah menjelaskan bahwa service adalah kontrak tertulis antara kamu dan systemd. Artikel ini menguji kontrak itu: apakah kontraknya benar-benar dijalankan, atau hanya terlihat rapi di atas kertas.

🎯 Tujuan

Setelah mengikuti praktikum ini, kamu bisa:

  • membedakan "perintah start diterima" dari "pekerjaan benar-benar berjalan"
  • membaca lima lapis bukti service sehat secara berurutan, tanpa menebak
  • mengenali arti kode keluar 200, 203, 217, dan 226
  • membedakan unit yang failed dari unit yang skipped
  • memperbaiki service yang kena start limit tanpa menghapus unitnya
  • menjelaskan ke guru atau atasan kenapa service gagal, bukan cuma "sudah saya restart"

πŸ“ Konteks nyata

Bayangkan kamu menyerahkan shift jaga lab ke teman. Kamu bilang "sudah saya serahkan", lalu pergi. Temanmu bilang "sudah saya terima", lalu duduk. Tapi lampu lab belum dinyalakan, pintu belum dikunci, dan AC belum hidup.

Itulah yang terjadi kalau kamu hanya melihat systemctl start berhasil. Perintahnya diterima, serah terimanya sah, tapi belum ada bukti pekerjaannya jalan.

πŸ”₯ Kasus nyata: sebuah SMK punya service penyalin nilai ujian yang jalan tiap sore. Suatu hari skripnya dipindah ke folder lain dan ExecStart= tidak diubah. systemctl start tetap menjawab sukses karena unit memakai Type=simple. Tiga hari nilai tidak tersalin, dan panitia menuduh server rapornya rusak.

🧠 Konsep singkat

πŸ”Ή Lima lapis bukti

Jangan melompat ke lapis berikutnya sebelum lapis sekarang terbukti. Kalau kamu melompat, kamu akan memperbaiki hal yang salah.

Lapis Pertanyaan Alat bukti
Unit terbaca systemd? | systemctl cat, kolom Loaded
Perintahnya benar-benar dieksekusi? | ExecMainStatus, Result
Prosesnya hidup? | MainPID, SubState
Karyanya nyata? | file log, port listen, isi database
Bertahan setelah reboot? | is-enabled, uji reboot

πŸ“Š Angka penting: systemctl start hanya menjawab satu hal, yaitu job sudah diterima. Ia tidak menjamin proses hidup, tidak menjamin pekerjaan selesai, dan tidak menjamin service hidup lagi setelah reboot.

πŸ”Ή Kenapa Type=simple membohongi kamu

Pada Type=simple, systemd menganggap unit sudah "started" tepat setelah proses di-fork(). Binary-nya bahkan belum tentu berhasil di-execve().

Dokumentasi resmi menyebutnya terang-terangan: perintah systemctl start untuk service simple akan melaporkan sukses walaupun binary-nya tidak bisa dijalankan, misalnya karena User= tidak ada atau file binary hilang.

Pada Type=exec, systemd menunggu sampai execve() berhasil. Di situlah kegagalan mulai terlihat di terminal, bukan cuma di journal.

πŸ”Ή Kode keluar sistem adalah kode pos masalah

Kalau ExecStart= gagal sebelum programmu sempat jalan, systemd memberi kode khusus. Kode ini menunjuk lapis mana yang rusak.

Kode Nama Arti
EXIT_CHDIR | Gagal pindah ke WorkingDirectory=
EXIT_EXEC | execve() gagal: file hilang atau tidak boleh dieksekusi
EXIT_GROUP | Gagal memakai Group=
EXIT_USER | Gagal memakai User=
EXIT_NAMESPACE | Gagal menyiapkan namespace

πŸ§ͺ Uji cepat: systemd-analyze exit-status 200 203 217 menampilkan daftar kode yang dikenal systemd di mesinmu, tanpa harus menghafal.

πŸ”Ή Start limit: kapan systemd berhenti mencoba

Kalau Restart= aktif dan service gagal terus, systemd tidak menyerah diam-diam. Ia menghitung percobaan start.

Bawaannya 5 kali start dalam 10 detik. Setelah itu unit dikunci dan Result= berubah menjadi start-limit-hit.

⚠️ Hati-hati: saat start limit tercapai, systemctl start menolak dengan pesan start request repeated too quickly. Banyak siswa panik lalu menghapus unit. Padahal cukup systemctl reset-failed, lalu perbaiki akar masalahnya.

🧩 Istilah penting

Istilah Arti sederhana
File kontrak .service yang dibaca systemd
File tambahan di nama.service.d/ untuk menimpa sebagian isi unit
Hasil akhir eksekusi terakhir: success, exit-code, timeout, start-limit-hit
Kode keluar proses utama pada eksekusi terakhir
Berapa kali systemd sudah merestart unit ini
PID proses utama; 0 berarti tidak ada proses hidup
Wadah kernel yang memuat semua proses milik unit
Syarat yang membuat unit dilewati, bukan digagalkan
Membuat symlink agar unit ikut start saat boot
Panggilan dari program ke systemd untuk bilang "saya siap"

πŸ”§ Praktik: Membangun Layanan dan Membuktikannya

Lab ini memakai Debian 12 di VM. Ambil snapshot dulu, karena kita akan sengaja merusak unit berkali-kali.

Semua nama diberi awalan lab- supaya tidak bentrok dengan service asli sistem.

πŸ”Ή Langkah 1: siapkan user dan skrip

Service yang rapi tidak jalan sebagai root. Kita buat user sistem khusus dan satu direktori log.

sudo useradd --system --no-create-home --shell /usr/sbin/nologin lab-lapor
sudo mkdir -p /var/log/lab-lapor
sudo chown lab-lapor:lab-lapor /var/log/lab-lapor
sudo chmod 750 /var/log/lab-lapor

Lalu buat skrip yang benar-benar bekerja: mencatat suhu tiap 10 detik dan berhenti dengan sopan saat diminta.

sudo tee /usr/local/bin/lab-lapor.sh > /dev/null <<'EOF'
#!/bin/bash
set -u
LOG=/var/log/lab-lapor/suhu.log
trap 'echo "$(date "+%F %T") berhenti" >> "$LOG"; exit 0' TERM INT
while true; do
  SUHU=$(cut -c1-2 /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo "00")
  echo "$(date '+%F %T') suhu=${SUHU}C" >> "$LOG"
  sleep 10
done
EOF
sudo chmod 755 /usr/local/bin/lab-lapor.sh

πŸ’‘ Tips: di VM, thermal_zone0 sering tidak ada. Skrip tetap jalan dan menulis suhu=00C. Yang kita uji adalah mekanisme service, bukan sensor.

πŸ”Ή Langkah 2: tulis unit dengan sengaja lengkap

[Unit]
Description=Lab pencatat suhu (praktikum systemd)
After=network.target
StartLimitIntervalSec=20s
StartLimitBurst=3

[Service]
Type=exec
User=lab-lapor
Group=lab-lapor
WorkingDirectory=/var/log/lab-lapor
EnvironmentFile=-/etc/lab-lapor.conf
ExecStart=/usr/local/bin/lab-lapor.sh
Restart=on-failure
RestartSec=2s
NoNewPrivileges=yes
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Simpan sebagai /etc/systemd/system/lab-lapor.service, lalu sudo systemctl daemon-reload.

Tanda minus pada EnvironmentFile=-/etc/lab-lapor.conf berarti "kalau file tidak ada, jangan gagal". Tanpa minus, unit langsung gagal saat file itu belum dibuat.

πŸ”Ή Langkah 3: periksa sebelum start

sudo systemd-analyze verify /etc/systemd/system/lab-lapor.service
systemctl cat lab-lapor.service
systemctl show -p Type,User,Restart,StartLimitBurst lab-lapor.service

Kalau verify tidak mengeluarkan apa pun, artinya tidak ada peringatan. Ini pemeriksaan termurah yang sering dilewatkan orang.

πŸ”Ή Langkah 4: kumpulkan lima bukti

sudo systemctl start lab-lapor.service
systemctl status lab-lapor.service --no-pager
systemctl show -p Result,ExecMainStatus,MainPID,NRestarts lab-lapor.service
tail -n 3 /var/log/lab-lapor/suhu.log
journalctl -u lab-lapor.service -b --no-pager -n 15

Yang harus kamu lihat: Active: active (running), Result=success, MainPID berisi angka, dan file log bertambah tiap 10 detik.

πŸ”Ή Langkah 5: buktikan bertahan setelah reboot

sudo systemctl enable --now lab-lapor.service
systemctl is-enabled lab-lapor.service
sudo reboot

Setelah masuk lagi, jalankan systemctl is-active lab-lapor dan lihat file log. Kalau active dan log bertambah tanpa kamu sentuh, baru layak disebut service.

πŸ§ͺ Uji cepat: systemctl list-units --failed harus kosong sebelum kamu mulai merusak apa pun. Baseline bersih membuat setiap kegagalan berikutnya jelas berasal dari ulahmu.

πŸ”Ή Uji A: binary tidak ada, dan janji Type=simple

Ubah lewat drop-in, bukan dengan mengedit file utama.

sudo systemctl edit --force --full lab-lapor.service

Ganti sementara Type=exec menjadi Type=simple dan arahkan ExecStart= ke file yang tidak ada:

Type=simple
ExecStart=/usr/local/bin/lab-lapor-salah.sh

Lalu:

sudo systemctl daemon-reload
sudo systemctl start lab-lapor.service; echo "exit=$?"
sleep 2
systemctl status lab-lapor.service --no-pager
systemctl show -p Result,ExecMainStatus lab-lapor.service

Hasil yang khas: echo "exit=$?" mencetak 0, tetapi status menunjukkan failed dengan status=203/EXEC.

Sekarang ubah kembali ke Type=exec dengan path yang sama salahnya. Perintah start langsung gagal dan terminal menampilkan pesan error, bukan sukses palsu.

Itulah bedanya: simple melaporkan "sudah saya fork", exec melaporkan "binary sudah benar-benar jalan".

πŸ”Ή Uji B: user tidak ada (217/USER)

sudo systemctl edit --force --full lab-lapor.service

Ganti User=lab-lapor menjadi User=lab-lapoor (salah ketik satu huruf), lalu daemon-reload dan start.

Bukti:

systemctl show -p Result,ExecMainStatus lab-lapor.service
journalctl -u lab-lapor.service -b --no-pager -n 10

Result=exit-code, ExecMainStatus=217, dan journal menyebut user tidak dikenal.

πŸ’‘ Tips: 217 hampir selalu berarti salah ketik nama user, bukan masalah izin file. Cek dengan id lab-lapoor sebelum menyalahkan sistem.

πŸ”Ή Uji C: working directory hilang (200/CHDIR)

Ganti WorkingDirectory=/var/log/lab-lapor menjadi /srv/tidak-ada.

systemctl show -p Result,ExecMainStatus lab-lapor.service

ExecMainStatus=200 muncul sebelum skripmu sempat berjalan. Artinya kegagalan ada di lapis 2, bukan di dalam skrip.

⚠️ Hati-hati: jangan menambahkan WorkingDirectory=-/srv/tidak-ada hanya agar "tidak error". Tanda minus membuat systemd diam saja, dan skripmu jalan di direktori yang salah. Gejalanya berubah jadi bug yang jauh lebih mahal.

πŸ”Ή Uji D: ExecStart relatif

Tulis ExecStart=lab-lapor.sh tanpa path absolut.

sudo systemd-analyze verify /etc/systemd/system/lab-lapor.service
sudo systemctl daemon-reload
sudo systemctl start lab-lapor.service

verify memberi peringatan bahwa path executable bukan absolut. Unit masuk keadaan bad-setting, dan start ditolak sebelum apa pun dijalankan.

Aturannya sederhana: systemd tidak memakai $PATH milik shellmu. Selalu tulis path lengkap.

πŸ”Ή Uji E: exit 1 berulang dan start limit

Buat skrip yang sengaja gagal:

sudo tee /usr/local/bin/lab-gagal.sh > /dev/null <<'EOF'
#!/bin/bash
echo "gagal dijalankan pada $(date '+%F %T')"
exit 1
EOF
sudo chmod 755 /usr/local/bin/lab-gagal.sh

Arahkan ExecStart= ke skrip itu, biarkan Restart=on-failure dan RestartSec=2s, lalu:

sudo systemctl daemon-reload
sudo systemctl start lab-lapor.service
sleep 20
systemctl status lab-lapor.service --no-pager
systemctl show -p Result,NRestarts lab-lapor.service

Setelah tiga percobaan dalam 20 detik, unit berhenti dicoba. Result=start-limit-hit, dan NRestarts menunjukkan jumlah restart yang sudah terjadi.

Sekarang coba start lagi:

sudo systemctl start lab-lapor.service

Muncul penolakan start request repeated too quickly. Ini bukan kerusakan; ini pengaman agar server tidak sibuk me-restart selamanya.

sudo systemctl reset-failed lab-lapor.service

Setelah direset, unit bisa dicoba lagi. Tapi ingat: yang kamu reset hanya penghitungnya, bukan penyebabnya.

πŸ”Ή Uji F: port sudah dipakai

Kegagalan di dalam program berbeda rasanya. Program jalan, lalu keluar sendiri karena sumber daya tidak tersedia.

sudo apt install -y python3
python3 -m http.server 8080 &

Buat unit kedua:

[Unit]
Description=Lab web statis
After=network.target

[Service]
Type=exec
User=lab-lapor
WorkingDirectory=/var/log/lab-lapor
ExecStart=/usr/bin/python3 -m http.server 8080
Restart=on-failure
RestartSec=2s

[Install]
WantedBy=multi-user.target

Simpan sebagai /etc/systemd/system/lab-web.service, lalu daemon-reload dan start.

Bukti: Result=exit-code, ExecMainStatus=1, dan journal memuat Address already in use. Perbaikannya bukan mengubah unit, tapi membebaskan port: matikan proses python3 yang kamu jalankan manual tadi.

πŸ“Š Angka penting: ExecMainStatus=0 berarti program keluar dengan sengaja dan bersih. Untuk service yang seharusnya hidup terus, itu juga kegagalan logika, meskipun bukan kegagalan sistem.

πŸ”Ή Uji G: Condition membuat unit di-skip, bukan gagal

Tambahkan syarat lewat drop-in:

[Unit]
ConditionPathExists=/srv/data-laporan

Direktori itu belum ada. Lalu daemon-reload dan start.

systemctl status lab-lapor.service --no-pager
systemctl show -p ConditionResult,ActiveState lab-lapor.service

Statusnya inactive (dead) dengan ConditionResult=no, bukan failed. Perintah start pun tidak melaporkan error.

⚠️ Hati-hati: inilah kegagalan paling berbahaya. Tidak ada alarm, tidak ada unit merah, dan monitoring yang hanya memeriksa is-failed akan tidur nyenyak sementara pekerjaan tidak pernah jalan.

Perbaikannya: buat direktori yang diminta, atau ganti pendekatan menjadi RequiresMountsFor= bila syaratnya adalah mount.

πŸ”Ή Uji H: Type=notify tanpa sd_notify

Program yang tidak pernah mengirim READY=1 akan membuat systemd menunggu.

[Service]
Type=notify
TimeoutStartSec=10s

TimeoutStartSec dinaikkan-turunkan hanya untuk lab. Bawaannya 90 detik, jadi tanpa ini kamu menunggu satu setengah menit per percobaan.

Setelah daemon-reload dan start, tunggu sepuluh detik. Hasilnya Result=timeout dan pesan start operation timed out. Proses dibunuh walaupun skripnya sehat.

πŸ’‘ Tips: Type=notify hanya untuk program yang benar-benar memanggil sd_notify(). Kalau kamu menyalin contoh dari internet tanpa memeriksa itu, kamu sedang membuat kegagalan baru.

πŸ”Ή Uji I: enable tanpa [Install]

Buat unit ketiga tanpa bagian [Install]:

[Unit]
Description=Lab tanpa install

[Service]
Type=oneshot
ExecStart=/usr/bin/true

Simpan sebagai lab-tanpa-install.service, lalu:

sudo systemctl daemon-reload
sudo systemctl start lab-tanpa-install.service
sudo systemctl enable lab-tanpa-install.service
systemctl is-enabled lab-tanpa-install.service

start berhasil, tetapi enable menjawab bahwa unit tidak punya konfigurasi instalasi. is-enabled mengembalikan static.

Artinya: service bisa dijalankan manual, tapi tidak akan pernah ikut boot. Ini penyebab paling umum dari kalimat "kemarin jalan, pagi ini mati".

πŸ”Ή Lembar hasil sembilan uji

Uji Gejala kunci Bukti utama
start sukses tapi unit merah | 203 di ExecMainStatus
gagal sebelum skrip jalan | 217
gagal sebelum skrip jalan | 200
start ditolak | LOAD: bad-setting
start ditolak setelah beberapa detik | Result=start-limit-hit
program keluar sendiri | ExecMainStatus=1
diam, tidak merah | ConditionResult=no
start menggantung lalu gagal | Result=timeout
jalan manual, mati setelah boot | is-enabled = static

🏭 Dipakai di dunia kerja

  • Sekolah dan lab CBT: layanan penyalin nilai serta penjadwal ujian harus enable dan punya Restart=on-failure. Kegagalan senyap di sini langsung terasa oleh ratusan siswa.
  • Kantor distributor dan retail: service sinkron stok ke ERP. Salah Type= membuat systemd merestart program yang sebenarnya sedang bekerja, dan stok tercatat dua kali.
  • Klinik dan rumah sakit: service backup ke disk eksternal. Syarat mount yang salah membuat backup berhenti berminggu-minggu tanpa satu pun alarm berbunyi.
  • ISP dan RT-RW Net: service monitoring pelanggan. Start limit yang tercapai menyembunyikan masalah uplink, karena unitnya sudah "menyerah" lebih dulu daripada jaringan.
  • Industri: gateway data mesin ke MQTT. Di sini urutan After= dan kesiapan dependency menentukan apakah data hilang saat listrik pulih.
  • Cloud dan VPS: aplikasi internal kecil lebih jujur dijalankan sebagai unit systemd daripada skrip di dalam screen yang lupa dipasang lagi setelah maintenance.

βœ… Verifikasi

Jalankan ini pada unit yang sehat, lalu simpan hasilnya sebagai baseline.

Perintah Hasil yang diharapkan
tanpa keluaran
enabled
active
success, 0, PID angka
kosong
baris bertambah tiap 10 detik
tanpa pesan error berulang

πŸ§ͺ Uji cepat: setelah reboot, cocokkan waktu baris terakhir di file log dengan uptime. Kalau log berhenti tepat saat mesin mati, service-mu tidak ikut boot.

⚠️ Kesalahan umum & troubleshooting

Gejala Periksa dulu Penyebab biasa
show -p Result,ExecMainStatus | Type=simple menelan kegagalan exec
ls -l path dan baris shebang | path salah, tidak executable, shebang hilang
id nama-user | user belum dibuat atau salah ketik
ls -ld direktori kerja | direktori belum ada atau tidak bisa diakses user
systemctl cat | path relatif atau dua baris ExecStart=
show -p NRestarts,Result | start limit tercapai
show -p ConditionResult | Condition tidak terpenuhi
journalctl -u ... -b | Type=notify tanpa sd_notify
systemctl is-enabled | belum enable atau tidak ada [Install]
systemctl cat | lupa daemon-reload
  • ⚠️ Hati-hati: systemctl restart yang diulang sepuluh kali bukan diagnosis. Ia hanya menghapus bukti dari eksekusi sebelumnya, karena status terakhir menimpa yang lama.
  • πŸ“Š Angka penting: default TimeoutStartSec adalah 90 detik, dan start limit bawaan 5 percobaan per 10 detik. Dua angka ini menjelaskan sebagian besar laporan "service-nya menggantung".
  • πŸ”₯ Kasus nyata: sebuah kantor distributor menyalin contoh unit Type=notify dari internet. Setiap pagi service antrean cetak menunggu 90 detik, dianggap gagal, lalu dijalankan ulang. Selama seminggu struk tercetak ganda, dan tim menyalahkan aplikasi kasir yang tidak pernah salah.

πŸ” Catatan keamanan & etika

  • File di /etc/systemd/system/ setara akses root saat unit dijalankan. Siapa pun yang bisa menulis ke folder itu bisa menjalankan perintah sebagai root pada boot berikutnya.
  • Jangan menaruh password di ExecStart=. Isinya terlihat di ps aux dan di systemctl cat. Pakai EnvironmentFile= dengan mode 600 milik root.
  • Biasakan User=, NoNewPrivileges=yes, dan PrivateTmp=yes. Untuk layanan yang hanya menulis satu folder, tambahkan ProtectSystem=strict plus ReadWritePaths=.
  • ConditionPathExists= bukan pengganti penanganan error. Unit yang dilewati tidak memberi alarm, jadi pastikan monitoring memeriksa hasil kerja, bukan hanya status unit.
  • Di server bersama, journalctl bisa memuat log milik layanan orang lain. Baca hanya unit yang menjadi tanggung jawabmu, dan jangan mengubah service milik orang lain tanpa izin.
  • Etika lab: semua uji rusak di artikel ini dilakukan di VM dengan snapshot. Jangan pernah melatih exit 1 dan start limit di server produksi.

πŸ“Œ Kesimpulan

Service yang sehat bukan yang berhasil di-start, tapi yang bisa kamu buktikan di lima lapis: unit terbaca, perintah dieksekusi, proses hidup, karya nyata, dan bertahan setelah reboot.

Kode keluar memberitahu lapis mana yang rusak. 200 berarti direktori, 203 berarti binary, 217 berarti user. Kalau semuanya benar tapi unit tetap diam, periksa ConditionResult sebelum menuduh apa pun.

Langkah berikutnya: ambil satu service nyata di labmu, catat Result, ExecMainStatus, dan NRestarts-nya hari ini, lalu bandingkan setelah satu minggu.

πŸ”— Materi terkait

πŸ“š Referensi