Selama tiga chapter terakhir kita memakai service buatan orang lain: memantaunya lewat systemctl, membaca lognya lewat journalctl, dan menatanya lewat target. Sekarang giliran kita membuat service sendiri. Bayangkan sebuah aplikasi — skrip backup, bot internal, atau server kecil — yang ingin selalu hidup, dinyalakan ulang otomatis saat mati, ikut menyala saat boot, dan meninggalkan log rapi tanpa konfigurasi tambahan. Semuanya kita dapatkan dengan menulis satu file unit. Chapter ini membedah anatomi unit service, menjalankan verifikasi statis untuk setiap tulisan kita, lalu membedah mekanisme enable sampai ke symlink-nya.

Anatomi Tiga Section #

File unit service adalah file teks INI dengan tiga section utama, dan masing-masing punya tugas yang jelas:

SectionTugasnya
[Unit]metadata dan relasi ke unit lain — Description, After, Wants, Requires
[Service]cara program dijalankan — Type, ExecStart, User, Restart
[Install]bagaimana unit dikaitkan ke target saat di-enable — WantedBy

Mari kita tulis unit pertama kita utuh dari nol — aplikasinya sengaja dibuat sederhana supaya fokusnya jatuh ke strukturnya:

bash
cat > aplikasi.sh << 'EOF'
#!/bin/bash
while true; do
  echo "denyut dari aplikasi latihan"
  sleep 5
done
EOF
chmod +x aplikasi.sh

cat > latihan.service << EOF
[Unit]
Description=Contoh service latihan

[Service]
Type=simple
ExecStart=$PWD/aplikasi.sh

[Install]
WantedBy=multi-user.target
EOF

systemd-analyze verify latihan.service && echo "unit lolos verifikasi statis"

Output yang Dihasilkan:

text
unit lolos verifikasi statis

Perintah systemd-analyze verify memeriksa file unit tanpa menjalankannya — seperti bash -n untuk shell script — dan echo di ujung && menandai bahwa pemeriksaan lolos. Mari kita bedah baris per baris:

  1. Baris 1-7 (cat > aplikasi.sh): aplikasi yang akan diawasi — loop tanpa akhir yang mencetak satu baris tiap lima detik, karena service memang untuk program yang hidup terus.
  2. Baris 8 (chmod +x aplikasi.sh): memberi bit eksekusi — systemd tidak menjalankan ExecStart lewat shell, jadi file harus benar-benar executable.
  3. Baris 12 (Description=): deskripsi manusiawi yang tampil di systemctl status.
  4. Baris 15 (Type=simple): bawaan — systemd menganggap service siap begitu prosesnya berjalan, jadi aplikasinya harus tetap di foreground.
  5. Baris 16 (ExecStart=$PWD/aplikasi.sh): perintah inti; path-nya wajib absolut. Heredoc ini sengaja tidak dikutip agar $PWD berkembang ke folder latihan — di sistem nyata tulis path permanen seperti /usr/local/bin/aplikasi.sh.
  6. Baris 19 (WantedBy=multi-user.target): saat di-enable, unit ini ditarik oleh multi-user.target.
  7. Baris 22 (systemd-analyze verify): pemeriksaan sintaks, absolut path, dan bit eksekusi — semua lolos.

Untuk memasang di sistem nyata: salin skrip ke /usr/local/bin/ dan unit ke /etc/systemd/system/ (folder /lib/systemd/system/ milik paket — jangan mengedit isinya karena akan tertimpa saat update), lalu jalankan sudo systemctl daemon-reload supaya systemd membaca ulang disk, dan nyalakan dengan systemctl enable --now latihan.service.

Mengatur Perilaku: Type dan Restart #

Directive Type= menentukan kapan systemd menganggap service “sudah jalan”, dan pilihannya harus cocok dengan perilaku aplikasi:

TypeCocok untuk
simpleaplikasi foreground modern (default)
forkingdaemon klasik yang mem-fork lalu keluar, gunakan PIDFile= agar systemd tahu induknya
oneshotskrip yang berjalan sekali lalu selesai — pasangan timer di Chapter systemd Timers
notifyaplikasi yang mengirim sinyal “ready” lewat sd_notify, seperti nginx

Sedangkan Restart= mengatur kebangkitan setelah kematian: on-failure (hidup lagi saat exit code bukan nol), on-abnormal (hanya saat sinyal atau timeout), always, atau no sebagai bawaan. Delay antar kebangkitan diatur RestartSec= — tanpa jeda, crash loop akan menghabiskan CPU dalam sekejap.

Lingkungan Eksekusi: User, WorkingDirectory, Environment #

Service berjalan sebagai root bila tidak dinyatakan lain — dan itu jarang yang kita inginkan. Directive di section [Service] memungkinkan kita mengunci identitas dan lingkungannya:

bash
cat > aplikasi.sh << 'EOF'
#!/bin/bash
while true; do
  echo "denyut dari aplikasi latihan"
  sleep 5
done
EOF
chmod +x aplikasi.sh
printf 'LOG_LEVEL=info\n' > latihan.env

cat > latihan.service << EOF
[Unit]
Description=Contoh service latihan
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=$PWD/aplikasi.sh
Restart=on-failure
RestartSec=5s
User=app
WorkingDirectory=$PWD
Environment=MODE=produksi
EnvironmentFile=$PWD/latihan.env

[Install]
WantedBy=multi-user.target
EOF

systemd-analyze verify latihan.service && echo "unit baru lolos verifikasi"
grep -E '^(User|WorkingDirectory|Environment)' latihan.service

Output yang Dihasilkan:

text
unit baru lolos verifikasi
User=app
WorkingDirectory=/home/budi
Environment=MODE=produksi
EnvironmentFile=/home/budi/latihan.env

Karena setiap blok latihan berdiri sendiri, blok ini menulis ulang aplikasi.sh dan latihan.env dengan cat/printf yang menimpa file lama — pembaca yang melompat langsung ke sini tetap mendapat hasil sama. Bedah barisnya:

  1. Baris 9 (printf 'LOG_LEVEL=info'): file environment — cukup baris KEY=value polos.
  2. Baris 14-15 (After= dan Wants=network-online.target): relasi ke unit lain, persis materi Chapter Service Dependencies .
  3. Baris 20-21 (Restart=on-failure, RestartSec=5s): bila aplikasi mati dengan exit code bukan nol, systemd menunggu lima detik lalu menyalakannya lagi; stop manual tetap dihormati.
  4. Baris 22 (User=app): proses berjalan sebagai user app, bukan root. Catatan: systemd-analyze verify tidak memeriksa keberadaan user — di sistem nyata buat dulu user-nya, karena service akan gagal start di tahap paling awal.
  5. Baris 23-25 (WorkingDirectory, Environment, EnvironmentFile): direktori kerja proses, variabel inline, dan variabel dari file — awali nama file dengan minus (EnvironmentFile=-/path) bila file itu boleh tidak ada.
  6. Baris 31-32 (systemd-analyze verify dan grep): verifikasi lolos, lalu grep menampilkan kembali directive lingkungan — $PWD telah menjadi path absolut di file jadinya.

Satu hal yang membedakan file environment dari file shell biasa: systemd mem-parsing isinya sendiri tanpa melewati shell, jadi tidak ada substitusi variabel maupun command substitution. Formatnya memang sengaja sederhana sehingga bisa juga disumber shell untuk menguji:

bash
printf 'LOG_LEVEL=info\n' > latihan.env
set -a
. latihan.env
set +a
printf 'service akan melihat LOG_LEVEL=%s\n' "$LOG_LEVEL"

Output yang Dihasilkan:

text
service akan melihat LOG_LEVEL=info

set -a membuat semua variabel yang dibaca ikut diekspor, meniru yang dialami proses service — cara cepat mengetes file environment sebelum dipasang.

Mengaitkan ke Target: Cara enable Bekerja #

Apa yang sebenarnya dilakukan systemctl enable? Chapter systemd Targets sudah menyinggungnya: pembuatan symlink di folder .wants milik target yang tertulis di [Install]. Kita bisa meniru mekanismenya dengan tangan untuk melihatnya dari dekat:

bash
printf '%s\n' '[Unit]' 'Description=Contoh service latihan' > latihan.service
mkdir -p multi-user.target.wants
ln -s ../latihan.service multi-user.target.wants/latihan.service
ls multi-user.target.wants/
readlink multi-user.target.wants/latihan.service

Output yang Dihasilkan:

text
latihan.service
../latihan.service

Symlink dibuat relatif (../latihan.service) persis seperti gaya systemctl, agar bundle folder tetap bisa dipindah-pindah. Di sistem nyata, hasil perintah aslinya begini:

text
Created symlink /etc/systemd/system/multi-user.target.wants/latihan.service → /etc/systemd/system/latihan.service.

Sejak symlink itu ada, setiap multi-user.target diaktifkan saat boot, latihan.service ikut ditarik. Dan begitu service hidup, semua peralatan chapter sebelumnya langsung bekerja — berikut tampilan status tipikalnya:

text
● latihan.service - Contoh service latihan
     Loaded: loaded (/etc/systemd/system/latihan.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-01-17 09:30:12 WIB; 42s ago
   Main PID: 3122 (aplikasi.sh)
      Tasks: 2 (limit: 4586)
     Memory: 1.2M (peak: 1.5M)
        CPU: 12ms
     CGroup: /system.slice/latihan.service
             ├─3122 /bin/bash /usr/local/bin/aplikasi.sh
             └─3123 sleep 5

Jan 17 09:30:12 laptop-budi systemd[1]: Started latihan.service - Contoh service latihan.
Jan 17 09:30:12 laptop-budi aplikasi.sh[3122]: denyut dari aplikasi latihan

Baris “denyut dari aplikasi latihan” itu bukan kebetulan: stdout service ditangkap journald otomatis, jadi journalctl -u latihan.service -f menampilkan denyutnya setiap lima detik tanpa konfigurasi apa pun.

Daftar directive lengkap dengan seluruh nilainya didokumentasikan di systemd.service(5) , directive lingkungan eksekusi di systemd.exec(5) , dan koleksi contoh unit siap contek di wiki Arch tentang systemd .

Unit yang baru kita tulis kini resmi bagian dari graf boot — dan perjalanan lengkapnya dari tombol power sampai layar login adalah kisah chapter berikutnya.