Menjalankan program di server itu gampang: ketik perintahnya, tekan Enter, selesai. Masalahnya muncul beberapa jam kemudian. Terminal ditutup, program ikut mati. Server reboot, program tidak pernah bangun lagi. Dan tidak ada yang tahu kalau program itu sudah berhenti sejak jam tiga pagi.

Di sinilah service berperan. Service bukan program yang lebih hebat, tapi kontrak tertulis yang memberitahu systemd: program apa yang dijalankan, sebagai user siapa, dari folder mana, dan apa yang harus dilakukan kalau program itu mati. Artikel ini membahas cara membuat kontrak itu sendiri, bukan sekadar memakai service bawaan paket.

🎯 Tujuan

Setelah membaca dan mempraktikkan artikel ini, kamu diharapkan bisa:

  • Menjelaskan beda "program yang dijalankan" dan "program yang dijadikan service".
  • Menulis file unit .service sendiri dengan tiga bagian yang benar.
  • Memilih Type= yang cocok: simple, oneshot, atau forking.
  • Memakai User= dan WorkingDirectory= supaya service tidak jalan sebagai root dan tidak salah folder.
  • Membedakan start, enable, dan enable --now.
  • Membaca kegagalan service dari systemctl status dan journalctl tanpa menebak-nebak.

πŸ“ Konteks nyata

Bayangkan kamu punya tukang panggilan. Selama kamu telepon, dia datang dan bekerja. Tapi begitu kamu berhenti mengawasi, dia pulang tanpa kabar. Kalau kamu sakit dan tidak bisa menelepon, tidak ada yang datang.

Program yang kamu jalankan manual persis seperti itu. Service adalah kontrak kerja dengan manajer gedung: jam masuk, nama petugas, ruang kerja, dan aturan "kalau tidak masuk tanpa kabar, hubungi saya". Manajer gedungnya adalah systemd.

πŸ’‘ Tips: Kalau kamu masih menjalankan skrip penting dengan nohup atau & di terminal SSH, itu tanda paling jelas bahwa skrip tersebut butuh service. nohup hanya menunda masalah, tidak menyelesaikannya.

πŸ”₯ Kasus nyata: Sebuah SMK punya skrip penyalin nilai dari aplikasi ujian ke server rapor. Skrip itu dijalankan manual oleh guru piket dengan nohup setiap sore. Suatu hari listrik padam malam hari, server hidup lagi pagi, tapi skrip tidak. Selama dua hari nilai tiga kelas tidak tersalin, dan panitia mengira server rapornya rusak. Setelah dijadikan service dengan Restart=on-failure, masalah yang sama tidak terulang saat listrik padam berikutnya.

🧠 Konsep singkat

File unit adalah file teks biasa berformat INI. Tidak ada bahasa pemrograman di dalamnya, hanya baris Kunci=nilai yang dikelompokkan dalam tiga bagian.

πŸ”Ή Tiga bagian yang wajib kamu kenal

Tabel berikut adalah kerangka minimal yang berlaku untuk hampir semua service.

Bagian Isi Menjawab pertanyaan
[Unit] Description, After, Wants Ini service apa, dan kapan boleh dijalankan?
[Service] Type, ExecStart, User, Restart Dijalankan bagaimana, oleh siapa, dari mana?
[Install] WantedBy=multi-user.target Hidup otomatis saat boot atau tidak?

Bagian [Install] punya sifat khusus yang sering disalahpahami: isinya tidak dibaca saat service berjalan. Bagian itu hanya dipakai ketika kamu menjalankan systemctl enable, untuk membuat symlink di multi-user.target.wants/.

πŸ”Ή Type: kapan systemd menganggap service "sudah jalan"

Ini keputusan paling penting, dan yang paling sering salah pilih.

Type Dianggap jalan saat Cocok untuk
simple proses sudah di-fork (default) program yang jalan terus, mis. daemon buatan sendiri
exec binary berhasil dieksekusi sama seperti simple, tapi gagal exec ikut dilaporkan
oneshot proses selesai skrip sekali jalan, migrasi, backup
forking proses induk keluar daemon lama yang mem-fork ke background
notify program mengirim sd_notify aplikasi modern yang siap melapor

⚠️ Hati-hati: Pada Type=simple, systemctl start bisa melapor sukses walaupun binary-nya tidak ada atau User=-nya tidak dikenal. Sebab systemd menganggap service jalan tepat setelah proses di-fork, sebelum eksekusi binary benar-benar terjadi. Kalau kamu ingin kegagalan itu langsung terlihat, pakai Type=exec.

πŸ“Š Angka penting: Default RestartSec= hanya 100 milidetik. Kalau programmu butuh waktu untuk melepas port atau menunggu database siap, restart serapat itu justru membuatnya gagal berulang. Isi sendiri, misalnya 5s.

πŸ”Ή Restart: siapa yang menghidupkan ulang

Restart= menentukan apakah systemd mencoba menyalakan ulang service yang berhenti. Nilai yang umum dipakai:

  • no β€” tidak pernah dihidupkan ulang. Cocok untuk oneshot.
  • on-failure β€” hanya kalau keluar dengan kode error atau dibunuh sinyal. Ini pilihan paling aman untuk pemula.
  • on-success β€” hanya kalau keluar bersih. Jarang dipakai.
  • always β€” selalu, termasuk setelah keluar normal. Cocok untuk program yang memang harus hidup terus.

Percobaan restart tidak tak terbatas. systemd punya pengaman bernama start limit yang diatur StartLimitIntervalSec= dan StartLimitBurst= di bagian [Unit]. Kalau batas terlampaui, service masuk status failed dengan pesan "start request repeated too quickly".

πŸ”₯ Kasus nyata: Sebuah kantor distributor memasang skrip sinkronisasi stok sebagai service dengan Restart=always dan RestartSec=1. Karena database belum siap saat boot, skrip gagal, restart, gagal lagi. Dalam satu menit service terkunci status failed dan tidak pernah mencoba lagi sampai admin sadar. Perbaikannya dua baris: RestartSec=10s dan After=network-online.target mariadb.service.

πŸ”Ή Di mana file unit disimpan

systemd mencari file unit di beberapa folder dengan urutan prioritas. Untuk service buatanmu sendiri, tempat yang benar adalah /etc/systemd/system/.

Lokasi Isi Siapa yang menulis
/etc/systemd/system/ unit buatan admin kamu
/usr/lib/systemd/system/ unit dari paket pembuat paket
/run/systemd/system/ unit sementara sistem saat runtime

Jangan pernah mengedit file di /usr/lib/systemd/system/. File itu milik paket dan akan tertimpa saat apt upgrade. Untuk mengubah perilaku service bawaan, pakai drop-in lewat systemctl edit nama.service.

πŸ”Ή Tiga perintah yang sering tertukar

  • systemctl start β€” jalankan sekarang, tidak otomatis saat boot.
  • systemctl enable β€” daftarkan agar jalan saat boot, tapi belum dijalankan sekarang.
  • systemctl enable --now β€” keduanya sekaligus. Ini yang paling sering kamu butuhkan.

Setiap kali file unit diubah, systemd perlu membaca ulang: systemctl daemon-reload. Tanpa itu, perubahan tidak berlaku dan kamu akan bingung kenapa perbaikannya "tidak ngaruh". Khusus systemctl edit, reload dilakukan otomatis.

🧩 Istilah penting

Istilah Arti singkat
Unit Satu objek yang dikelola systemd, ditandai akhiran seperti .service
Unit file File teks berisi definisi unit, biasanya di /etc/systemd/system
Drop-in File tambahan di nama.service.d/*.conf untuk menimpa sebagian isi unit
ExecStart Perintah utama yang dijalankan saat service start
WantedBy Target yang "menginginkan" unit ini, dasar pembuatan symlink saat enable
Journal Kumpulan log yang dikelola systemd-journald, dibaca dengan journalctl
Masked Unit yang dinonaktifkan total lewat symlink ke /dev/null
Start limit Batas jumlah percobaan start sebelum systemd menyerah

Satu istilah lagi yang perlu kamu hafal: multi-user.target. Itu nama sistem "siap dipakai banyak user tanpa GUI", pengganti istilah runlevel 3 di zaman SysV.

πŸ”§ Praktik: membuat service pertama

Kita akan membuat service pemantau sederhana: mencatat suhu dan pemakaian disk ke file log setiap 60 detik. Tujuannya bukan alat monitoring serius, tapi supaya kamu melihat sendiri semua lapisan bekerja.

πŸ”Ή Langkah 1: siapkan skrip dan user khusus

Buat user sistem tanpa shell login, supaya service tidak jalan sebagai root.

sudo useradd --system --no-create-home --shell /usr/sbin/nologin lapor
sudo mkdir -p /var/lib/lapor
sudo chown lapor:lapor /var/lib/lapor

Lalu buat skripnya di /usr/local/bin/lapor-suhu.sh:

#!/usr/bin/env bash
LOG=/var/log/lapor-suhu.log

while true; do
  SUHU=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo n/a)
  DISK=$(df -h / | awk 'NR==2 {print $5}')
  echo "$(date '+%F %T') suhu=$SUHU disk=$DISK" >> "$LOG"
  sleep 60
done
sudo install -m 755 -o root -g root /dev/null /usr/local/bin/lapor-suhu.sh
sudo nano /usr/local/bin/lapor-suhu.sh   # tempel isi di atas
sudo touch /var/log/lapor-suhu.log
sudo chown lapor:lapor /var/log/lapor-suhu.log

πŸ’‘ Tips: Skrip boleh milik root dan mode 755. Yang dijalankan sebagai user lapor adalah prosesnya, bukan pemilik file. Ini praktik yang aman: user biasa tidak bisa mengubah skrip yang dijalankan service.

πŸ”Ή Langkah 2: tulis file unit

Simpan sebagai /etc/systemd/system/lapor-suhu.service:

[Unit]
Description=Lapor suhu dan pemakaian disk tiap menit
Documentation=man:systemd.service(5)
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3

[Service]
Type=simple
User=lapor
Group=lapor
WorkingDirectory=/var/lib/lapor
ExecStart=/usr/local/bin/lapor-suhu.sh
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Perhatikan tiga hal yang sering dilupakan:

  • ExecStart= harus path absolut. ExecStart=./lapor-suhu.sh ditolak dengan pesan "Executable path is not absolute".
  • WorkingDirectory= menentukan folder kerja. Tanpa ini, path relatif di dalam skrip mengarah ke /.
  • User= membuat proses tidak berjalan sebagai root. Kalau user tidak ada, service gagal dengan status 217/USER.

πŸ”Ή Langkah 3: periksa sebelum menyalakan

systemd-analyze verify /etc/systemd/system/lapor-suhu.service
sudo systemctl daemon-reload
sudo systemctl start lapor-suhu.service
systemctl status lapor-suhu.service --no-pager

Status yang sehat kira-kira berbunyi Active: active (running) dengan Main PID terisi. Kalau tertulis failed, jangan langsung mengubah-ubah file; baca dulu baris Process: ... status=... di bawahnya.

πŸ”Ή Langkah 4: buktikan lewat log dan file

journalctl -u lapor-suhu.service -n 20 --no-pager
tail -n 5 /var/log/lapor-suhu.log

Kalau log jurnal kosong tapi file log terisi, itu wajar: skrip menulis sendiri ke file, bukan ke stdout. Justru ini bukti service berjalan.

πŸ”Ή Langkah 5: jadikan otomatis saat boot

sudo systemctl enable --now lapor-suhu.service
systemctl is-enabled lapor-suhu.service
systemctl is-active lapor-suhu.service

Keluaran enabled dan active adalah dua hal berbeda. enabled menjawab "apakah dia ikut boot", active menjawab "apakah dia hidup sekarang".

πŸ”Ή Langkah 6: ubah tanpa menyentuh file asli

Misalnya kamu ingin restart policy lebih longgar. Jangan edit file utama, pakai drop-in:

sudo systemctl edit lapor-suhu.service

Isi yang muncul adalah file kosong di /etc/systemd/system/lapor-suhu.service.d/override.conf. Tulis hanya bagian yang berubah:

[Service]
RestartSec=30s

Lalu systemctl cat lapor-suhu.service untuk melihat hasil gabungan file asli dan drop-in. Inilah cara yang benar untuk menyesuaikan service milik paket, misalnya Nginx atau SSH.

πŸ”Ή Langkah 7: uji kegagalan dengan sengaja

Service yang tidak pernah diuji gagal biasanya gagal di waktu yang paling tidak tepat.

sudo systemctl stop lapor-suhu.service
sudo mv /usr/local/bin/lapor-suhu.sh /root/
sudo systemctl start lapor-suhu.service
systemctl status lapor-suhu.service --no-pager
sudo mv /root/lapor-suhu.sh /usr/local/bin/
sudo systemctl reset-failed lapor-suhu.service

πŸ§ͺ Uji cepat: Setelah skrip dikembalikan, jalankan systemctl reset-failed lalu systemctl start. Kalau kamu lupa reset-failed, service bisa menolak start karena masih terhitung dalam start limit.

🏭 Dipakai di dunia kerja

  • Sekolah dan lab komputer: skrip sinkronisasi nilai, penarik absensi, atau pembersih folder ujian dijalankan sebagai service oneshot agar tidak bergantung pada guru yang ingat menekan Enter.
  • Kantor dan distributor: aplikasi internal buatan sendiri (antrian cetak, ekspor data harian) dijalankan sebagai service dengan User= khusus, sehingga mati saat listrik padam bukan berarti mati permanen.
  • Rumah sakit dan klinik: agen pengirim data ke sistem BPJS atau server pusat harus hidup terus; Restart=on-failure plus log jurnal adalah syarat minimal sebelum tim IT mau menyerahkannya ke user.
  • ISP dan penyedia layanan: skrip kecil pemantau link, pengirim metrik, atau pembaru sertifikat dijalankan terjadwal sebagai service atau timer, bukan dari terminal admin yang kebetulan masih terbuka.
  • Industri dan pabrik: agen penghubung antara mesin PLC dan dashboard produksi berjalan sebagai service; berhentinya agen berarti dashboard kosong padahal mesin tetap jalan.
  • Cloud dan VPS: hampir semua panduan deployment modern berakhir dengan "buat service systemd", karena itu cara paling standar menjaga proses tetap hidup setelah sesi SSH ditutup.

βœ… Verifikasi

Jangan puas dengan "sepertinya jalan". Periksa satu per satu.

Perintah Yang dilihat Harapan
systemctl status lapor-suhu Active, Main PID active (running)
systemctl is-enabled lapor-suhu status boot enabled
journalctl -u lapor-suhu -b log sejak boot ada catatan start tanpa error
systemctl show -p User -p Restart lapor-suhu nilai efektif sesuai file unit
systemd-analyze verify lapor-suhu.service validitas unit tidak ada peringatan
ps -o user,pid,cmd -C lapor-suhu.sh user proses lapor, bukan root

Uji terakhir yang paling jujur: reboot server, lalu cek systemctl status sebelum menyentuh apa pun. Kalau service hanya hidup setelah kamu start manual, berarti enable-nya belum benar.

⚠️ Kesalahan umum & troubleshooting

Gejala Penyebab umum Perbaikan
status=203/EXEC skrip tidak executable atau shebang salah chmod 755, cek baris #!/usr/bin/env bash
status=217/USER User= menunjuk user yang tidak ada buat user atau perbaiki nama
status=200/CHDIR WorkingDirectory= tidak ada buat foldernya atau hapus direktif
"Executable path is not absolute" ExecStart= pakai path relatif tulis /usr/local/bin/...
Jalan manual bisa, service gagal beda user, PATH, atau folder kerja samakan User= dan WorkingDirectory=
Perubahan tidak berefek lupa daemon-reload systemctl daemon-reload
"start request repeated too quickly" restart beruntun melewati start limit perbaiki penyebab, lalu reset-failed
Service mati saat SSH ditutup masih dijalankan manual di shell jadikan unit, jangan nohup
Log kosong di jurnal program menulis ke file sendiri periksa file log aplikasi

⚠️ Hati-hati: Kalau service oneshot selesai, statusnya kembali dead dan itu normal. Jangan panik dan menambahkan Restart=always pada skrip migrasi; itu akan menjalankan migrasi berulang-ulang. Untuk oneshot yang ingin tetap tampak aktif, gunakan RemainAfterExit=yes.

⚠️ Hati-hati: Pesan "jalan manual bisa, tapi service gagal" hampir selalu bukan bug systemd. Penyebabnya perbedaan lingkungan: user berbeda, PATH berbeda, folder kerja berbeda, atau variabel dari .bashrc tidak ikut terbaca. Service tidak membaca .bashrc milik siapa pun.

πŸ” Catatan keamanan & etika

File unit di /etc/systemd/system/ dieksekusi dengan hak root. Siapa pun yang bisa menulis ke folder itu setara bisa menjalankan perintah sebagai root, jadi jaga permission folder dan jangan longgarkan tanpa alasan.

  • Selalu isi User= dan Group=. Service buatan sendiri hampir tidak pernah perlu root.
  • Jangan menaruh password di file unit. File unit terbaca semua user. Kalau perlu rahasia, pakai EnvironmentFile= dengan permission 600.
  • Tambahkan pengerasan ringan: NoNewPrivileges=yes, PrivateTmp=yes, dan ProtectSystem=strict bila service tidak perlu menulis ke sistem.
  • Untuk service yang hanya butuh satu proses sederhana, DynamicUser=yes bisa membuat user otomatis tanpa kamu mengelola akun.
  • Etika: membuat service di komputer lab sekolah untuk menjalankan program yang bukan tugasmu, termasuk pelanggaran. Kamu diberi akses root untuk belajar, bukan untuk menguasai mesin.

πŸ“Š Angka penting: Nama unit maksimal 255 karakter, dan hanya boleh berisi huruf, angka, titik, garis bawah, tanda hubung, titik dua, dan backslash. Spasi diganti dengan aturan escaping tersendiri; pakai systemd-escape kalau ragu.

πŸ“Œ Kesimpulan

Service bukan sihir dan bukan program yang lebih pintar. Service adalah kontrak: kamu menyebut program, user, folder, dan aturan restart; systemd yang mengawasi sisanya.

Tiga hal yang paling menentukan berhasil atau tidaknya service buatanmu: ExecStart absolut, User= dan WorkingDirectory= yang benar, serta daemon-reload setelah setiap perubahan. Sisanya hanya latihan membaca status dan log.

Langkah berikutnya: ambil satu skrip yang biasa kamu jalankan manual di lab, lalu ubah menjadi service dengan Type=oneshot. Rasakan bedanya setelah kamu reboot server dan skrip itu berjalan sendiri.

πŸ”— Materi terkait

πŸ“š Referensi