Shell Scripting untuk Automasi Server

Shell Scripting untuk Automasi Server

Bitnesia • Aug 27, 2026 86 •EN

Bab 34 menutup Bagian VIII dengan checklist keamanan yang merangkum lima lapisan defense in depth sejak Bab 30. Bagian IX yang dimulai dari bab ini mengambil arah yang berbeda. Alih-alih menambah lapisan pertahanan baru, kita mengangkat satu kebiasaan yang sebenarnya sudah berulang kali dipraktikkan sepanjang series ini tanpa pernah dibahas secara utuh, yaitu shell scripting untuk automasi server. Bab 5 memperkenalkan systemd timer sebagai pengganti cron sambil berjanji membahasnya lebih dalam di sini. Bab 23 menulis script bash yang mengotomasi backup empat database sekaligus, lengkap dengan baris set -euo pipefail yang penjelasan mendalamnya sengaja ditunda sampai bab ini. Bab ini menepati kedua janji tersebut sekaligus membuka Bagian IX, gerbang menuju Infrastructure as Code yang akan berlanjut ke Ansible di Bab 36 dan cloud-init di Bab 37.

Skenarionya familier bagi Sysadmin mana pun yang pernah mengelola server produksi. Sebuah script cleanup yang ditulis buru-buru berjalan dua kali sekaligus lewat cron karena eksekusi sebelumnya belum selesai, lalu menghapus file yang justru sedang ditulis proses lain. Script backup yang "kelihatan" berhasil padahal salah satu tahapnya gagal diam-diam, karena tidak ada satu baris pun yang menghentikan eksekusi begitu terjadi error. Kedua insiden ini punya akar masalah yang sama: script ditulis seolah selalu dijalankan manusia yang mengawasi outputnya, padahal tujuannya justru berjalan tanpa pengawasan lewat cron atau systemd timer.

Kita akan mulai dengan menyegarkan cepat konvensi script shell yang aman untuk automasi, dilanjutkan menulis tiga script administrasi yang lazim dibutuhkan sehari-hari yaitu backup file konfigurasi, cleanup log, dan health check server. Bagian inti bab ini membedah error handling dan logging secara mendalam, termasuk trap dan flock yang belum pernah disinggung sebelumnya. Kita menutup bab dengan menjadwalkan seluruh script tersebut lewat cron dan systemd timer, sekaligus kapan sebaiknya memilih salah satu di antara keduanya.

35.1 Review Shell Scripting untuk Automasi Server

Series prasyarat Linux 101 sudah mengenalkan dasar shell scripting, jadi bagian ini bukan pengulangan penuh. Fokusnya menyegarkan konvensi yang relevan ketika sebuah script tidak lagi dijalankan manual oleh manusia di depan terminal, melainkan dipicu otomatis oleh cron atau systemd timer tanpa ada siapa pun yang mengawasi outputnya secara langsung.

35.1.1 Konvensi Script yang Aman untuk Dijalankan Otomatis

Beberapa kebiasaan yang mungkin terasa opsional saat menulis script untuk dijalankan manual justru jadi wajib begitu script itu dijadwalkan otomatis. Shebang #!/bin/bash di baris pertama menentukan interpreter yang dipakai, dan sebaiknya eksplisit memakai bash alih-alih sh karena fitur seperti array dan [[ ]] yang akan kita pakai di bab ini adalah ekstensi bash, bukan bagian dari POSIX shell standar. Path harus ditulis absolut, bukan relatif, karena cron dan systemd menjalankan script dari non-interactive shell yang tidak membaca ~/.bashrc maupun PATH yang biasa aktif di sesi SSH interaktif kita. Variable yang menampung path sebaiknya selalu dikutip dengan tanda kutip ganda seperti "$VAR", karena path yang mengandung spasi akan pecah jadi beberapa argumen terpisah tanpa tanda kutip tersebut. Terakhir, exit code punya arti penting di konteks automasi: 0 menandakan sukses, angka selain itu menandakan gagal, dan systemd maupun cron mengandalkan angka ini untuk mendeteksi apakah sebuah job benar-benar berhasil, bukan dari ada tidaknya output di layar yang toh tidak ada yang membacanya secara langsung.

Langkah Praktik

  1. Buat direktori tempat menyimpan seluruh script automasi yang akan ditulis sepanjang bab ini.
    sudo mkdir -p /usr/local/bin
  2. Buat file template yang menjadi dasar seluruh script pada bagian berikutnya.
    sudo nano /usr/local/bin/template.sh
  3. Isi dengan struktur minimal berikut.
    #!/bin/bash
    #
    # Template dasar script automasi
    
    set -euo pipefail
    
    log() {
      echo "[$(date '+%F %T')] $*"
    }
    
    main() {
      log "Script dimulai"
      log "Script selesai"
    }
    
    main "$@"
    Baris set -euo pipefail dan fungsi log() sengaja belum dijelaskan detail di sini; keduanya dibedah tuntas di Bagian 35.3.
  4. Ubah izin file agar dapat dieksekusi, lalu jalankan.
    sudo chmod +x /usr/local/bin/template.sh
    /usr/local/bin/template.sh

Verifikasi dan Troubleshooting

  • Periksa exit code setelah script selesai berjalan; angka 0 menandakan sukses.
    echo $?
  • Error Permission denied saat menjalankan script hampir selalu berarti langkah chmod +x terlewat.
  • Jika script memanggil binary yang terpasang di lokasi tidak umum seperti /snap/bin, jangan mengandalkan PATH default. Tulis path lengkapnya langsung di dalam script, kebiasaan yang menjadi sangat relevan begitu kita masuk ke penjadwalan cron di Bagian 35.4.1.

35.2 Script untuk Task Administrasi Umum

Tiga skenario berikut adalah task administrasi yang paling sering ditulis ulang oleh Sysadmin di berbagai server: backup file di luar database, pembersihan file yang menumpuk, dan pengecekan kesehatan server secara berkala. Ketiganya memakai struktur template dari Bagian 35.1.1 sebagai basis.

35.2.1 Backup Otomatis File dan Direktori Konfigurasi

Bab 23 sudah membahas backup khusus database lewat pg_dump, mysqldump, dan tool sejenisnya. File konfigurasi di /etc serta direktori webroot memerlukan pendekatan berbeda, karena isinya bukan dump database melainkan kumpulan file dan direktori biasa yang lebih cocok dikemas dengan tar.

Langkah Praktik

  1. Buat script backup konfigurasi.
    sudo nano /usr/local/bin/backup-config.sh
  2. Isi dengan script berikut, sesuaikan SOURCE_DIRS dengan direktori yang relevan di server ini.
    #!/bin/bash
    set -euo pipefail
    
    BACKUP_DIR="/var/backups/config"
    SOURCE_DIRS=("/etc" "/var/www")
    RETENTION_DAYS=14
    TIMESTAMP=$(date +%Y%m%d-%H%M%S)
    
    mkdir -p "$BACKUP_DIR"
    chmod 700 "$BACKUP_DIR"
    
    tar czf "$BACKUP_DIR/config-$TIMESTAMP.tar.gz" "${SOURCE_DIRS[@]}"
    chmod 600 "$BACKUP_DIR/config-$TIMESTAMP.tar.gz"
    
    find "$BACKUP_DIR" -type f -name "config-*.tar.gz" -mtime +"$RETENTION_DAYS" -delete
    
    echo "Backup selesai: config-$TIMESTAMP.tar.gz"
    Pola retention dengan find -mtime +N -delete ini persis seperti yang sudah dipraktikkan di Bagian 23.6.2, hanya diterapkan ke file tar.gz alih-alih dump database. Dua baris chmod di atas bukan sekadar formalitas: /etc yang ikut di-backup memuat /etc/shadow berisi hash password seluruh user, sehingga arsip hasil backup ini sama sensitifnya dengan file aslinya. Tanpa pembatasan izin eksplisit, file tar.gz yang baru dibuat mengikuti umask default sistem yang pada banyak server masih menghasilkan izin 644, alias bisa dibaca semua user lokal, sebuah pelanggaran nyata terhadap prinsip least privilege yang sudah kita bahas di Bagian 34.2.
  3. Ubah izin file agar dapat dieksekusi, lalu uji jalankan.
    sudo chmod +x /usr/local/bin/backup-config.sh
    sudo /usr/local/bin/backup-config.sh

Verifikasi dan Troubleshooting

  • Pastikan file arsip baru muncul dengan izin yang sudah dibatasi, dan isinya benar.
    ls -lh /var/backups/config
    tar tzf /var/backups/config/config-*.tar.gz | head
    Kolom izin pada ls -lh semestinya menampilkan -rw------- untuk file tar.gz-nya, hasil dari chmod 600 pada script.
  • GNU tar biasanya juga mencetak baris peringatan tar: Removing leading '/' from member names ke layar saat mengemas path absolut seperti /etc. Ini bukan tanda kegagalan, hanya pemberitahuan bahwa tar menyimpan path secara relatif di dalam arsip, kebiasaan standar yang justru membuat hasil restore-nya lebih aman karena tidak otomatis menimpa /etc yang asli begitu saja saat diekstrak.
  • Catatan penting dari lapangan soal tar: exit code 1 bukan selalu berarti backup gagal total. GNU tar mengembalikan 1 ketika ada file yang berubah persis saat sedang dibaca, misalnya log aplikasi yang terus ditulis proses lain, sementara exit code 2 baru menandakan kegagalan fatal seperti direktori sumber tidak ditemukan. Karena set -e menganggap exit code selain 0 sebagai kegagalan, hal ini bisa membuat script berhenti untuk peringatan yang sebenarnya tidak fatal. Poin ini dibedah lebih jauh di Bagian 35.3.1.
  • Jika muncul Permission denied saat membaca isi /etc, jalankan script dengan sudo seperti pada langkah praktik, karena sebagian file di dalamnya memang sengaja dibatasi hanya bisa dibaca root.

35.2.2 Cleanup Log dan File Temporer

logrotate yang dibahas tuntas di Bab 39 menangani rotasi log standar dengan baik, tetapi tidak semua file perlu dirotasi. File temporer aplikasi, hasil export sementara, atau log kustom yang formatnya tidak dikenali logrotate tetap memerlukan script pembersihan sendiri.

Langkah Praktik

  1. Buat script cleanup dengan dukungan mode dry-run, yaitu mode simulasi yang hanya menampilkan file yang akan dihapus tanpa benar-benar menghapusnya.
    sudo nano /usr/local/bin/cleanup-temp.sh
  2. Isi dengan script berikut.
    #!/bin/bash
    set -euo pipefail
    
    TARGET_DIR="/var/log/myapp"
    MAX_AGE_DAYS=30
    MODE="${1:-}"
    
    if [[ "$MODE" == "--dry-run" ]]; then
      echo "Mode dry-run, tidak ada file yang dihapus"
      find "$TARGET_DIR" -type f -mtime +"$MAX_AGE_DAYS" -print
    else
      find "$TARGET_DIR" -type f -mtime +"$MAX_AGE_DAYS" -delete
      echo "File lebih dari $MAX_AGE_DAYS hari di $TARGET_DIR sudah dihapus"
    fi
    Argumen $1 ditangkap lewat "${1:-}", bukan "$1" polos, karena set -u pada baris set -euo pipefail membuat bash berhenti dengan error begitu argumen ini tidak diisi sama sekali. Sintaks ${1:-} memberi nilai default berupa string kosong jika argumennya memang tidak ada.
  3. Ubah izin file agar dapat dieksekusi, lalu selalu uji lebih dulu dengan dry-run sebelum menjalankan versi yang benar-benar menghapus file.
    sudo chmod +x /usr/local/bin/cleanup-temp.sh
    sudo /usr/local/bin/cleanup-temp.sh --dry-run
  4. Setelah daftar file pada langkah dry-run sudah dipastikan benar, jalankan tanpa argumen untuk menghapus sungguhan.
    sudo /usr/local/bin/cleanup-temp.sh

Verifikasi dan Troubleshooting

  • Kebiasaan menjalankan dry-run lebih dulu ini sengaja ditekankan karena pengalaman lapangan yang berulang: banyak insiden salah hapus file produksi berawal dari script cleanup yang langsung dijalankan tanpa pernah diperiksa dulu daftar targetnya.
  • Jika direktori target belum ada, find akan melempar error No such file or directory. Pastikan TARGET_DIR sudah disesuaikan dengan direktori nyata di server ini sebelum dijalankan.
  • Untuk kebutuhan pembersihan generik pada direktori /tmp, systemd sendiri sudah menyediakan mekanisme bawaan lewat systemd-tmpfiles yang dikonfigurasi di /etc/tmpfiles.d/. Script kustom seperti di atas tetap lebih cocok dipakai ketika logika pembersihannya spesifik untuk satu aplikasi, misalnya perlu memeriksa pola nama file tertentu sebelum menghapus.

35.2.3 Health Check Server Sederhana

Sebelum masuk ke tool monitoring modern seperti Netdata dan Prometheus di Bab 38, script health check sederhana sudah cukup untuk mendeteksi dua masalah paling umum di server produksi, yaitu disk yang hampir penuh dan service kritikal yang berhenti tanpa terdeteksi.

Langkah Praktik

  1. Buat script health check.
    sudo nano /usr/local/bin/health-check.sh
  2. Isi dengan script berikut, sesuaikan daftar SERVICES dengan service yang benar-benar berjalan di server ini.
    #!/bin/bash
    set -euo pipefail
    
    DISK_THRESHOLD=85
    SERVICES=("nginx" "postgresql")
    
    check_disk() {
      local usage
      usage=$(df --output=pcent / | tail -1 | tr -dc '0-9')
      if (( usage >= DISK_THRESHOLD )); then
        logger -t health-check -p user.crit "Disk root sudah $usage%, di atas ambang batas $DISK_THRESHOLD%"
        echo "PERINGATAN: disk root $usage%"
      fi
    }
    
    check_services() {
      for svc in "${SERVICES[@]}"; do
        if ! systemctl is-active --quiet "$svc"; then
          logger -t health-check -p user.crit "Service $svc tidak aktif"
          echo "PERINGATAN: service $svc tidak aktif"
        fi
      done
    }
    
    check_disk
    check_services
    echo "Health check selesai"
    Perintah logger mengirim pesan langsung ke systemd journal dengan tag health-check dan prioritas crit, sehingga hasil pengecekan tetap tercatat rapi walau tidak ada satu baris echo pun yang sempat terbaca oleh manusia secara langsung.
  3. Ubah izin file agar dapat dieksekusi, lalu uji jalankan.
    sudo chmod +x /usr/local/bin/health-check.sh
    sudo /usr/local/bin/health-check.sh

Verifikasi dan Troubleshooting

  • Cari catatan yang sudah dikirim logger ke journal berdasarkan tag-nya.
    journalctl -t health-check -n 20
  • Uji jalur peringatan service dengan menghentikan sementara salah satu service dari daftar SERVICES, jalankan lagi script tersebut, lalu aktifkan kembali service tersebut setelah pengujian selesai agar server tidak tertinggal dalam kondisi mati.
  • Perlu disampaikan secara jujur soal batasan script ini: script ini hanya mencatat kondisi kritis ke journal, bukan mengirim notifikasi aktif lewat email atau chat. Alerting yang benar-benar mendorong notifikasi ke Sysadmin dibahas lebih lengkap lewat tool khusus di Bagian 38.4, karena membangun jalur notifikasi yang andal (email, webhook, atau integrasi chat) memerlukan infrastruktur tambahan di luar cakupan satu script sederhana.
  • Dua pengecekan di atas sengaja dijaga minimal agar mudah dipahami. Di lapangan, fungsi serupa biasa diperluas dengan pengecekan memory lewat free -m atau beban sistem lewat uptime, mengikuti pola check_disk() dan check_services() yang sama persis: bandingkan angka terhadap ambang batas, lalu catat lewat logger jika terlampaui.

35.3 Error Handling dan Logging di Script

Ketiga script pada Bagian 35.2 semuanya memakai set -euo pipefail tanpa penjelasan mendalam, mengikuti kebiasaan yang sama seperti script backup database di Bagian 23.6.2. Bagian ini akhirnya membedah tuntas apa yang sebenarnya terjadi di balik baris itu, ditambah dua kebiasaan lain yang sama pentingnya untuk script yang berjalan tanpa pengawasan yaitu trap dan flock.

35.3.1 set -euo pipefail Secara Mendalam

Baris set -euo pipefail sebenarnya adalah tiga opsi bash yang digabung jadi satu, dan masing-masing menutup celah yang berbeda.

  • -e menghentikan script begitu ada perintah yang keluar dengan exit code selain 0. Tanpa opsi ini, bash secara default akan terus lanjut ke baris berikutnya meski satu perintah di tengah gagal, seolah tidak terjadi apa-apa. Perlu dicatat, -e punya pengecualian yang sering membingungkan: perintah yang jadi bagian dari kondisi if/while, bagian dari daftar && atau ||, atau didahului tanda !, tidak menghentikan script meski gagal, karena bash menganggap kegagalannya memang sengaja diperiksa secara eksplisit di titik itu. Baris if ! systemctl is-active --quiet "$svc" di Bagian 35.2.3 memanfaatkan pengecualian ini secara sengaja.
  • -u menghentikan script begitu ada variable yang belum pernah didefinisikan tapi dipakai. Pesan MONGO_USER: unbound variable yang muncul di Bagian 23.6.2 adalah contoh nyata opsi ini bekerja, dan pola "${1:-}" di Bagian 35.2.2 adalah cara aman menangkap argumen opsional tanpa memicu error tersebut.
  • -o pipefail mengubah cara bash menilai keberhasilan sebuah pipeline. Tanpa opsi ini, exit code sebuah pipeline hanya diambil dari perintah paling kanan, sehingga mysqldump | gzip pada Bagian 23.6.2 akan tetap dianggap sukses meski mysqldump di sisi kiri sebenarnya gagal total, selama gzip sendiri tidak ikut gagal. pipefail membuat exit code pipeline mengikuti perintah manapun di dalamnya yang gagal, bukan hanya yang terakhir.

Ketiganya bekerja sama membuat script menerapkan prinsip fail fast, yaitu berhenti secepat mungkin begitu ada yang tidak beres, alih-alih melanjutkan eksekusi dengan asumsi keliru bahwa semuanya baik-baik saja. Prinsip ini krusial untuk script yang dijalankan otomatis tanpa pengawasan, karena tidak ada manusia yang membaca output-nya secara langsung untuk menyadari sesuatu sudah salah di tengah jalan. Satu pengecualian yang jujur perlu diketahui, seperti kasus exit code 1 dari tar di Bagian 35.2.1, adalah beberapa tool memang memakai exit code non-nol untuk peringatan yang sebenarnya tidak fatal. Untuk kasus semacam itu, tambahkan penanganan eksplisit alih-alih membiarkan set -e menghentikan script secara tidak semestinya, misalnya dengan memeriksa exit code secara manual lewat $? dan hanya menghentikan script jika nilainya benar-benar di atas ambang yang dianggap fatal.

35.3.2 trap untuk Cleanup dan Penanganan Error

trap adalah builtin bash yang menjalankan sebuah perintah begitu script menerima sinyal tertentu. Dua sinyal yang paling relevan untuk automasi adalah EXIT, yang selalu terpicu begitu script berhenti apa pun penyebabnya, dan ERR, yang khusus terpicu saat sebuah perintah gagal dan diikuti opsi -e dari Bagian 35.3.1. Kombinasi keduanya memastikan proses cleanup tetap berjalan dan detail error tetap tercatat, bahkan ketika script berhenti mendadak di tengah jalan.

Langkah Praktik

  1. Modifikasi template.sh dari Bagian 35.1.1 untuk menambahkan penanganan trap.
    sudo nano /usr/local/bin/template.sh
  2. Tambahkan fungsi dan trap berikut tepat di bawah set -euo pipefail.
    TMP_FILE=$(mktemp)
    
    cleanup() {
      log "Menghapus file temporer: $TMP_FILE"
      rm -f "$TMP_FILE"
    }
    trap cleanup EXIT
    
    error_handler() {
      logger -t template-script -p user.err "Gagal di baris $1 saat menjalankan: $2"
    }
    trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
    Variable $LINENO berisi nomor baris tempat error terjadi, sedangkan $BASH_COMMAND berisi perintah persis yang sedang dijalankan saat itu. Keduanya hanya valid dipakai di dalam trap ERR, bukan di sembarang bagian script.
  3. Uji trap dengan sengaja menyisipkan perintah yang pasti gagal, misalnya false, lalu jalankan.
    echo 'false' >> /usr/local/bin/template.sh
    sudo /usr/local/bin/template.sh; echo "exit code: $?"

Verifikasi dan Troubleshooting

  • Pastikan pesan error tercatat di journal lengkap dengan nomor baris.
    journalctl -t template-script -n 5
  • Perhatikan baris log Menghapus file temporer: /tmp/tmp.xxxxxxxxxx yang tercetak di output langkah sebelumnya, lalu salin path lengkapnya untuk konfirmasi file itu sudah benar-benar hilang, meski script berhenti karena error, tanda trap EXIT tetap berjalan.
    ls /tmp/tmp.xxxxxxxxxx 2>&1
    Path aslinya acak setiap kali script dijalankan karena dibuat oleh mktemp, jadi ganti tmp.xxxxxxxxxx dengan nama file persis yang muncul di output pada perangkat masing-masing.
  • Hapus baris false percobaan setelah pengujian selesai, karena baris itu sengaja disisipkan hanya untuk memicu kegagalan.
  • Jangan menaruh perintah yang berpotensi gagal di dalam fungsi cleanup() itu sendiri. Trap EXIT hanya berjalan sekali, dan jika perintah di dalamnya ikut gagal, proses cleanup bisa berhenti di tengah jalan tanpa ada trap lain yang menangkapnya.

35.3.3 Logging Terstruktur dan Mencegah Job Tumpang Tindih dengan flock

Dua kebiasaan terakhir ini sering luput dari script yang ditulis terburu-buru, padahal keduanya adalah sumber insiden yang cukup umum ditemui di lapangan. Logging yang hanya mengandalkan echo polos akan hilang begitu saja ketika script dijalankan lewat cron tanpa redirect eksplisit, dan tanpa mekanisme lock file, job yang jadwalnya tumpang tindih dengan eksekusi sebelumnya yang belum selesai bisa berjalan bersamaan, menciptakan race condition di mana dua proses berebut menulis ke resource yang sama.

Langkah Praktik

  1. Tambahkan mekanisme lock di awal health-check.sh dari Bagian 35.2.3, tepat di bawah set -euo pipefail.
    sudo nano /usr/local/bin/health-check.sh
  2. Sisipkan baris berikut.
    LOCK_FILE="/run/lock/health-check.lock"
    exec 200>"$LOCK_FILE"
    if ! flock -n 200; then
      echo "Instance lain sedang berjalan, keluar"
      exit 1
    fi
    exec 200>"$LOCK_FILE" membuka file lock lewat file descriptor nomor 200, nomor yang sengaja dipilih tinggi untuk menghindari bentrok dengan descriptor standar (0, 1, 2) yang sudah dipakai bash sendiri. flock -n 200 mencoba mengunci descriptor tersebut tanpa menunggu (-n untuk non-blocking); jika gagal karena instance lain sudah memegang lock yang sama, script langsung keluar dengan exit code 1. Lock ini otomatis terlepas begitu script selesai, tanpa perlu dihapus manual.
  3. Uji dengan menjalankan script dua kali hampir bersamaan di dua terminal berbeda, atau simulasikan lewat perintah berikut.
    sudo /usr/local/bin/health-check.sh & sudo /usr/local/bin/health-check.sh
    wait

Verifikasi dan Troubleshooting

  • Salah satu dari dua eksekusi pada langkah pengujian semestinya menampilkan pesan Instance lain sedang berjalan, keluar dan keluar dengan exit code 1, sementara yang satunya berjalan normal sampai selesai.
  • Terapkan pola flock yang sama ke backup-config.sh dan cleanup-temp.sh dari Bagian 35.2, terutama untuk server dengan volume data besar di mana proses backup berpotensi berjalan lebih lama dari interval jadwalnya sendiri.
  • Untuk logging terstruktur yang konsisten, kombinasikan fungsi log() dari Bagian 35.1.1 dengan logger seperti pada health-check.sh: echo untuk output yang terbaca saat pengujian manual, logger untuk pencatatan permanen ke journal yang nantinya bisa disentralisasi lewat rsyslog di Bab 39.

35.4 Menjadwalkan Script dengan Cron dan Systemd Timer

Bab 5 dan Bab 23 sudah mempraktikkan systemd timer secara langsung untuk menjadwalkan job. Bagian ini melengkapi sisi yang belum pernah dibahas, yaitu cron, sekaligus menutup bab dengan panduan memilih di antara keduanya.

35.4.1 Cron dan Crontab

Cron sudah jadi standar penjadwalan job di sistem Unix selama puluhan tahun, dan paket cron pada umumnya sudah terpasang secara default di Ubuntu Server 26.04 LTS. crontab -e membuka editor untuk mengubah jadwal milik user yang sedang login, dengan format lima kolom waktu diikuti perintah: menit, jam, tanggal, bulan, dan hari dalam minggu, masing-masing bisa diisi angka spesifik, tanda * untuk "setiap", atau step seperti */15 untuk "setiap 15 satuan". Cron juga mendukung string pintasan seperti @reboot, @daily, dan @hourly untuk jadwal yang umum, tanpa perlu menulis lima kolom lengkap.

Langkah Praktik

  1. Pasang paket cron jika belum terpasang; perintah ini aman dijalankan meski paketnya sudah ada, apt hanya akan melaporkan versi terbaru sudah terpasang.
    sudo apt install cron
    systemctl status cron
  2. Buka crontab milik user root. health-check.sh perlu berjalan sebagai root supaya systemctl is-active pada Bagian 35.2.3 bisa memeriksa seluruh service secara konsisten dan lock file di /run/lock dari Bagian 35.3.3 bisa dibuat tanpa error izin, sejalan dengan cara script ini diuji manual lewat sudo sepanjang bab ini.
    sudo crontab -e
  3. Tambahkan baris berikut untuk menjalankan health check setiap 15 menit, dengan output dialihkan eksplisit ke file log.
    */15 * * * * /usr/local/bin/health-check.sh >> /var/log/health-check-cron.log 2>&1
    Redirect 2>&1 ini penting karena environment cron tidak terhubung ke terminal mana pun. Tanpa redirect eksplisit, cron sebenarnya tetap menangkap output tersebut lalu mencoba mengirimkannya lewat email ke alamat yang diisikan di variable MAILTO pada crontab, atau ke user pemilik crontab jika MAILTO tidak diisi. Pada praktiknya, pengiriman ini sering gagal diam-diam kalau server belum punya MTA seperti Postfix terpasang, sehingga output yang tidak di-redirect eksplisit maupun ditangani logger berisiko hilang begitu saja tanpa jejak.
  4. Simpan lalu konfirmasikan jadwal sudah tercatat.
    sudo crontab -l

Verifikasi dan Troubleshooting

  • Tunggu hingga jadwal berikutnya tiba, lalu periksa log eksekusi cron itu sendiri lewat tag CRON yang otomatis dikirim ke journal.
    journalctl -t CRON -n 20
  • Periksa juga isi file log yang sudah ditentukan di langkah ketiga.
    cat /var/log/health-check-cron.log
  • Job yang berjalan normal lewat terminal tapi gagal lewat cron biasanya disebabkan PATH yang jauh lebih terbatas di environment cron dibanding sesi shell interaktif. Pastikan setiap binary yang dipanggil dari dalam script sudah ditulis dengan path absolut, sesuai kebiasaan yang sudah ditekankan di Bagian 35.1.1.
  • Selain crontab per-user, entri terjadwal juga bisa ditulis langsung di /etc/cron.d/ untuk kebutuhan sistem-wide. Formatnya sama seperti crontab biasa, hanya ditambah satu kolom nama user di antara kolom waktu dan perintah.

35.4.2 Memilih antara Cron dan Systemd Timer

Kedua tool sama-sama valid, dan perbedaannya lebih soal fitur yang dibutuhkan daripada mana yang "lebih baik" secara mutlak.

AspekCronSystemd Timer
LoggingPerlu redirect manual ke file atau loggerOtomatis masuk journalctl lewat unit service-nya
Job yang terlewat saat server matiTidak dijalankan ulang kecuali dibantu anacron tambahanPersistent=true otomatis menjalankan begitu server menyala kembali
Dependency antar serviceTidak didukung langsungBisa memakai After=, Requires= seperti Bagian 5.4
Jumlah file konfigurasiSatu baris di crontabDua unit file terpisah (.timer dan .service)
PortabilitasTersedia luas di hampir semua sistem mirip UnixHanya tersedia di sistem berbasis systemd

Pada Ubuntu Server modern, systemd timer jadi pilihan default yang masuk akal untuk job penting di production, terutama yang butuh jaminan tetap jalan meski server sempat mati saat jadwalnya tiba, seperti backup database di Bagian 23.7. Cron tetap relevan untuk automasi cepat dan sederhana, terutama saat mengikuti dokumentasi resmi sebuah tool pihak ketiga yang sudah menyediakan contoh entri cron siap pakai, atau ketika script yang sama perlu dijalankan juga di sistem non-systemd. Sebagai tambahan, systemd timer juga mendukung penjadwalan relatif lewat directive OnBootSec dan OnUnitActiveSec, berguna untuk job yang cukup dijalankan beberapa menit setelah boot atau beberapa jam sejak eksekusi terakhirnya, tanpa perlu terikat jam pasti seperti OnCalendar yang sudah dipraktikkan sejak Bagian 5.3.1.

Verifikasi dan Troubleshooting

  • Lihat seluruh job terjadwal yang aktif di server ini, baik lewat cron maupun systemd timer, sebagai langkah audit sebelum menutup bab ini.
    sudo crontab -l
    systemctl list-timers
  • Hindari menjadwalkan job yang sama lewat cron dan systemd timer sekaligus. Duplikasi semacam ini sering luput diperhatikan di server yang sudah dikelola bergantian oleh beberapa Sysadmin, dan berpotensi memicu race condition yang sama seperti dibahas di Bagian 35.3.3, meski masing-masing script sudah dilindungi flock.

Sampai di sini, kita punya fondasi automasi yang solid untuk satu server: script yang ditulis dengan konvensi aman, tiga contoh task administrasi siap pakai, error handling yang tidak lagi gagal diam-diam, serta dua pilihan scheduler untuk menjalankannya tanpa campur tangan manual. Batasannya juga mulai terasa di titik ini. Setiap script di atas hanya berlaku untuk satu server, dan menyalin baris demi baris ke server lain begitu jumlah server bertambah jelas bukan pendekatan yang bisa bertahan lama. Bab 36 membuka jawabannya lewat Ansible, tool Infrastructure as Code yang menjalankan konfigurasi serupa ke banyak server sekaligus dari satu control node, dimulai dari konsepnya sebelum masuk ke instalasi dan playbook pertama.