Mengelola Sumber Daya Sistem dengan Perintah `ulimit` di Linux
Perintah `ulimit` adalah utilitas shell bawaan pada sistem Unix dan Linux yang memberlakukan batas sumber daya per-proses dan per-pengguna, mencegah satu proses atau pengguna menghabiskan sumber daya sistem seperti waktu CPU, memori, deskriptor file terbuka, dan jumlah proses. Perintah ini beroperasi di tingkat kernel melalui system call `setrlimit()`, menjadikannya salah satu mekanisme paling langsung dan berbiaya rendah yang tersedia bagi administrator sistem untuk tata kelola sumber daya.
Untuk server mana pun yang menjalankan beban kerja produksi — baik aplikasi web dengan lalu lintas tinggi, mesin database, maupun tumpukan layanan mikro yang dikontainerisasi — pengaturan `ulimit` yang salah konfigurasi atau tidak ada adalah penyebab utama kegagalan berantai, proses yang tidak terkendali, dan pemadaman sistem penuh. Mengatur batas ini dengan benar bukan pilihan; ini adalah kebersihan infrastruktur yang mendasar.
Cara Kerja `ulimit` di Balik Layar
Ketika proses shell memanggil `ulimit`, ia memanggil system call `getrlimit()` dan `setrlimit()` yang didefinisikan dalam standar POSIX. Setiap batas direpresentasikan sebagai sepasang nilai: soft limit dan hard limit. Nilai-nilai ini disimpan per-proses dalam deskriptor proses kernel dan diwarisi oleh proses anak pada saat `fork()`.
Model pewarisan ini sangat penting untuk dipahami. Jika Anda menetapkan nilai `ulimit` dalam sesi shell, setiap proses yang dihasilkan dari shell tersebut — termasuk daemon yang diluncurkan melalui skrip init — mewarisi batas tersebut. Sebaliknya, batas yang ditetapkan di `/etc/security/limits.conf` berlaku pada waktu login PAM, bukan pada saat runtime, yang berarti batas tersebut hanya berlaku untuk sesi login baru, bukan untuk layanan yang sudah berjalan.
Soft Limit vs. Hard Limit
| Properti | Soft Limit | Hard Limit |
|---|---|---|
| — | — | — |
| Siapa yang dapat menaikkannya | Pengguna tanpa hak istimewa mana pun (hingga hard limit) | Hanya root (`CAP_SYS_RESOURCE`) |
| Siapa yang dapat menurunkannya | Pengguna mana pun | Pengguna mana pun (tidak dapat dibalikkan tanpa root) |
| Penegakan | Ditegakkan oleh kernel | Bertindak sebagai batas atas untuk soft limit |
| Kasus penggunaan umum | Batas operasional sehari-hari | Maksimum absolut untuk kebijakan keamanan |
| Flag dalam `ulimit` | `-S` | `-H` |
Kesalahan operasional yang umum adalah menetapkan hard limit sama dengan soft limit. Ini menghilangkan semua fleksibilitas bagi proses untuk sementara menaikkan batasnya sendiri, yang dilakukan secara sah oleh beberapa aplikasi (seperti implementasi JVM tertentu dan mesin database) selama startup.
Referensi Lengkap: Flag Sumber Daya `ulimit`
| Flag | Sumber Daya | Satuan | Nilai Produksi Umum |
|---|---|---|---|
| — | — | — | — |
| `-t` | Waktu CPU | Detik | `unlimited` untuk daemon |
| `-f` | Ukuran file maksimum | Blok 512-byte | `unlimited` atau batas tertentu |
| `-d` | Ukuran segmen data (heap) | KB | `unlimited` untuk aplikasi Java |
| `-s` | Ukuran stack | KB | `8192` (default) |
| `-c` | Ukuran file core dump | Blok 512-byte | `0` (dinonaktifkan di produksi) |
| `-m` | Ukuran resident set maksimum | KB | Jarang ditegakkan (gunakan cgroups) |
| `-v` | Memori virtual (ruang alamat) | KB | `unlimited` untuk sebagian besar layanan |
| `-n` | Deskriptor file terbuka | Jumlah | `65536` atau lebih tinggi untuk server sibuk |
| `-u` | Proses pengguna maksimum | Jumlah | `4096`–`65536` tergantung peran |
| `-l` | Memori terkunci (mlock) | KB | Tinggi untuk Redis, Elasticsearch |
| `-i` | Sinyal tertunda | Jumlah | Default sistem biasanya cukup |
| `-q` | Byte antrian pesan POSIX | Byte | Default sistem |
| `-r` | Prioritas penjadwalan real-time | Prioritas | `0` kecuali beban kerja RT |
| `-e` | Prioritas penjadwalan maksimum (nice) | Nilai nice | Default sistem |
Penggunaan `ulimit` Praktis dengan Konteks Dunia Nyata
Melihat Batas Saat Ini
“`bash
ulimit -a # All soft limits for the current shell
ulimit -aH # All hard limits for the current shell
“`
Untuk memeriksa batas proses yang sedang berjalan (PID) secara spesifik, baca langsung dari filesystem proc — ini adalah sumber otoritatif dan melewati pelaporan tingkat shell:
“`bash
cat /proc/<PID>/limits
“`
Ini sangat berharga saat memecahkan masalah layanan yang dimulai oleh systemd atau skrip init, di mana `ulimit -a` tingkat shell tidak akan mencerminkan batas aktual proses.
Menetapkan Soft dan Hard Limit
“`bash
Set soft limit for open file descriptors
ulimit -Sn 65536
Set hard limit for open file descriptors
ulimit -Hn 131072
Set both simultaneously (soft = hard = value)
ulimit -n 65536
“`
Menonaktifkan Core Dump di Produksi
“`bash
ulimit -c 0
“`
Core dump dapat menghabiskan gigabyte ruang disk dalam hitungan detik ketika proses bermeori tinggi mengalami crash. Menonaktifkannya di produksi adalah praktik standar kecuali Anda sedang aktif melakukan debugging. Untuk lingkungan pengembangan, tetapkan jalur khusus menggunakan `sysctl kernel.core_pattern` bersama dengan batas core yang bukan nol.
Membatasi Waktu CPU untuk Proses yang Tidak Dipercaya
“`bash
ulimit -t 30
“`
Ini mengirimkan `SIGXCPU` ke proses ketika mencapai soft limit waktu CPU, dan `SIGKILL` pada hard limit. Ini sangat berguna di lingkungan shared hosting atau saat menjalankan skrip yang dikirimkan pengguna.
Menaikkan Batas Deskriptor File Terbuka untuk Layanan dengan Konkurensi Tinggi
Nginx, HAProxy, PostgreSQL, dan Redis semuanya memerlukan jumlah deskriptor file terbuka yang tinggi di bawah beban. Default sistem sebesar 1024 sangat rendah dan berbahaya untuk produksi:
“`bash
ulimit -n 65536
“`
Namun, ini hanya memengaruhi sesi shell saat ini. Untuk konfigurasi persisten, gunakan metode yang dijelaskan di bagian berikutnya.
Membuat Pengaturan `ulimit` Menjadi Persisten
Metode 1: `/etc/security/limits.conf`
Ini adalah pendekatan berbasis PAM standar untuk batas persisten tingkat pengguna:
“`
/etc/security/limits.conf
<domain> <type> <item> <value>
- soft nofile 65536
- hard nofile 131072
nginx soft nproc 4096
nginx hard nproc 8192
postgres soft nofile 65536
postgres hard nofile 65536
postgres soft memlock unlimited
postgres hard memlock unlimited
“`
Wildcard `*` berlaku untuk semua pengguna tetapi tidak berlaku untuk root. Root memerlukan entri eksplisit:
“`
root soft nofile 65536
root hard nofile 131072
“`
Pastikan modul PAM dimuat. Verifikasi `/etc/pam.d/common-session` (Debian/Ubuntu) atau `/etc/pam.d/system-auth` (RHEL/CentOS) mengandung:
“`
session required pam_limits.so
“`
Metode 2: File Drop-in `/etc/security/limits.d/`
Untuk manajemen yang lebih bersih, terutama dalam sistem manajemen konfigurasi seperti Ansible atau Puppet, tempatkan file batas khusus layanan di direktori drop-in:
“`bash
/etc/security/limits.d/99-nginx.conf
nginx soft nofile 65536
nginx hard nofile 131072
“`
File dalam direktori ini diproses setelah `limits.conf` dan menimpanya, menjadikannya ideal untuk penyesuaian khusus aplikasi tanpa memodifikasi konfigurasi dasar.
Metode 3: Unit Layanan systemd (Standar Modern)
Untuk layanan yang dikelola oleh systemd — yang merupakan mayoritas distribusi Linux modern — `limits.conf` tidak diterapkan secara default. systemd mengelola batas sumber dayanya sendiri per unit layanan:
“`ini
/etc/systemd/system/nginx.service.d/limits.conf
[Service]
LimitNOFILE=65536
LimitNPROC=4096
LimitCORE=0
LimitMEMLOCK=infinity
“`
Setelah mengedit, muat ulang dan mulai ulang:
“`bash
systemctl daemon-reload
systemctl restart nginx
“`
Verifikasi batas yang diterapkan:
“`bash
cat /proc/$(systemctl show -p MainPID nginx | cut -d= -f2)/limits
“`
Ini adalah metode paling andal untuk layanan produksi dan harus menjadi pendekatan default pada sistem mana pun yang menjalankan systemd (Ubuntu 16.04+, CentOS 7+, Debian 8+).
Metode 4: File Profil Shell
Untuk batas sesi pengguna yang berlaku secara interaktif, tambahkan perintah `ulimit` ke `/etc/profile` (seluruh sistem) atau `~/.bashrc` / `~/.profile` (per-pengguna). Pendekatan ini sesuai untuk workstation pengembang tetapi tidak cocok untuk proses daemon.
Profil Konfigurasi `ulimit` Berbasis Peran
Peran server yang berbeda memerlukan profil batas sumber daya yang pada dasarnya berbeda. Menerapkan default generik di semua jenis server adalah sumber umum kegagalan yang halus dan sulit didiagnosis.
Web Server (Nginx / Apache)
“`
nofile: 65536–131072 # High concurrency requires many open sockets + files
nproc: 4096 # Worker processes + threads
core: 0 # Disable core dumps in production
“`
Database Relasional (PostgreSQL / MySQL)
“`
nofile: 65536 # Many concurrent connections = many file descriptors
memlock: unlimited # Required for shared memory and huge pages
nproc: 4096
stack: 8192 KB
core: 0
“`
Server Aplikasi Java (Tomcat / Spring Boot)
“`
nofile: 65536
nproc: 65536 # JVM thread-per-connection models spawn many threads
data: unlimited # JVM heap is allocated from the data segment
stack: 512 KB # Reduce stack size to fit more threads in memory
“`
Redis / Penyimpanan Data In-Memory
“`
nofile: 65536
memlock: unlimited # Prevents swapping of memory-mapped data
“`
Jebakan Kritis dan Kasus Tepi
Batas `nproc` menghitung thread, bukan hanya proses. Di Linux, thread diimplementasikan sebagai proses ringan (`clone()` dengan memori bersama). Aplikasi Java dengan 500 thread dihitung sebagai 500 terhadap batas `nproc`. Ini mengejutkan banyak administrator yang menetapkan nilai `nproc` yang konservatif dan kemudian bertanya-tanya mengapa JVM mereka crash dengan `OutOfMemoryError: unable to create new native thread`.
`ulimit -v` membatasi ruang alamat virtual, bukan RAM fisik. Banyak administrator menetapkan `-v` dengan berpikir mereka membatasi penggunaan memori. Pada kenyataannya, mereka membatasi ruang alamat virtual, yang mencakup file yang dipetakan ke memori, pustaka bersama, dan metaspace JVM. Menetapkan ini terlalu rendah akan menyebabkan kegagalan `mmap()` dan kesalahan aplikasi yang tidak jelas.
`ulimit` tidak berlaku secara retroaktif. Mengubah batas di `limits.conf` atau file unit systemd tidak memengaruhi proses yang sudah berjalan. Anda harus memulai ulang layanan agar batas baru berlaku.
Lingkungan container melewati `ulimit` dengan cara yang tidak terduga. Di Docker, default `ulimit` ditetapkan di tingkat daemon (`/etc/docker/daemon.json`) dan dapat ditimpa per container dengan `–ulimit`. Namun, batas container dibatasi oleh batas kernel host. Menetapkan `nofile=1048576` dalam container sementara host memiliki `nofile=65536` akan secara diam-diam kembali ke batas host.
Batas sistem `nofile` terpisah dari batas per-proses. Parameter kernel `fs.file-max` (ditetapkan melalui `sysctl`) mengontrol jumlah total deskriptor file di seluruh sistem. Bahkan jika `nofile` per-proses ditetapkan tinggi, mencapai `fs.file-max` akan menyebabkan kesalahan `ENFILE` di seluruh sistem. Periksa dan sesuaikan keduanya:
“`bash
sysctl fs.file-max
sysctl -w fs.file-max=2097152
“`
`ulimit` vs. cgroups: Memilih Alat yang Tepat
| Kemampuan | `ulimit` / `setrlimit` | cgroups v2 |
|---|---|---|
| — | — | — |
| Cakupan | Per-proses (diwarisi oleh anak) | Per-kelompok proses |
| Pembatasan memori | Ruang alamat virtual saja (`-v`) | Penegakan RSS + swap aktual |
| Pembatasan CPU | Anggaran waktu CPU (`-t`) | Pengontrol bandwidth CPU (% yang tepat) |
| Pembatasan I/O | Tidak didukung | Bobot dan batas kecepatan block I/O |
| Pembatasan jaringan | Tidak didukung | Memerlukan integrasi tc + cgroup |
| Persistensi | Melalui PAM atau systemd | Melalui slice systemd atau cgroupfs |
| Kompatibilitas container | Terbatas | Native (Docker, Kubernetes menggunakan cgroups) |
| Granularitas | Kasar | Terperinci |
`ulimit` tetap menjadi alat yang tepat untuk batas per-sesi yang cepat, batas deskriptor file, dan kontrol core dump. Untuk isolasi sumber daya yang komprehensif — terutama di lingkungan multi-tenant atau beban kerja yang dikontainerisasi — cgroups v2 adalah mekanisme yang lebih unggul. Pada lingkungan VPS Hosting atau Dedicated Server yang dikonfigurasi dengan baik, kedua mekanisme biasanya digunakan secara kombinasi: `ulimit` untuk penjaga per-proses dan cgroups untuk anggaran sumber daya agregat.
Pemantauan dan Validasi Batas Sumber Daya
Pemantauan proaktif mencegah kegagalan terkait batas menjadi insiden produksi.
Periksa penggunaan deskriptor file saat ini di seluruh sistem:
“`bash
cat /proc/sys/fs/file-nr
Output: <allocated> <unused> <max>
“`
Temukan proses yang mendekati batas `nofile` mereka:
“`bash
for pid in /proc/[0-9]*; do
pid_num=${pid##*/}
limit=$(awk '/Max open files/{print $4}' /proc/$pid_num/limits 2>/dev/null)
current=$(ls /proc/$pid_num/fd 2>/dev/null | wc -l)
[ -n "$limit" ] && [ "$limit" != "unlimited" ] &&
awk -v c=$current -v l=$limit -v p=$pid_num
'BEGIN{if(c/l>0.8) printf "PID %s: %d/%d (%.0f%%)n",p,c,l,c/l*100}'
done
“`
Alat untuk pemantauan berkelanjutan:
- `lsof -u <username>` — daftar semua file terbuka untuk pengguna
- `ss -s` — statistik socket (berkorelasi dengan tekanan `nofile`)
- `htop` dengan tampilan pohon proses — visualisasikan jumlah proses per pengguna
- `sar -v` — penggunaan deskriptor file dan inode historis melalui sysstat
- Prometheus `node_exporter` — mengekspos metrik `node_filefd_allocated` dan `node_filefd_maximum` untuk peringatan
Untuk lingkungan yang menjalankan VPS dengan cPanel atau panel kontrol lainnya, banyak dari batas ini telah dikonfigurasi sebelumnya oleh penginstal panel, tetapi sering kali perlu disesuaikan ke atas seiring pertumbuhan lalu lintas. Selalu verifikasi batas aktual terhadap `/proc/<PID>/limits` daripada mempercayai dokumentasi panel.
Implikasi Keamanan dari `ulimit`
Batas sumber daya juga merupakan kontrol keamanan. Tanpa batas tersebut, proses yang dikompromikan atau bermasalah dapat mengeksekusi fork bomb (`:(){ :|:& };:`), menghabiskan semua slot proses yang tersedia dan membuat sistem tidak responsif. Batas `nproc` yang konservatif per pengguna adalah mitigasi utama:
“`
- hard nproc 4096
“`
Demikian pula, menonaktifkan core dump (`-c 0`) mencegah konten memori yang sensitif — termasuk kunci enkripsi, kata sandi, dan token sesi — ditulis ke disk dalam file yang dapat dibaca semua orang.
Untuk lingkungan shared hosting atau server mana pun di mana beberapa pengguna memiliki akses shell, `ulimit` adalah lapisan keamanan yang wajib. Pada infrastruktur Shared Web Hosting, batas ini biasanya ditegakkan di tingkat platform, tetapi administrator yang menjalankan VPS multi-pengguna mereka sendiri harus mengonfigurasinya secara eksplisit.
Jika server Anda menangani terminasi SSL atau manajemen sertifikat, pastikan proses yang menangani TLS (misalnya, Nginx, HAProxy) memiliki batas `nofile` yang cukup, karena setiap koneksi TLS memerlukan beberapa deskriptor file. Padukan ini dengan SSL Certificates yang dikonfigurasi dengan benar untuk menghindari kegagalan koneksi terkait sertifikat yang memperparah masalah sumber daya.
Untuk penerapan server email, Postfix dan Dovecot sangat sensitif terhadap batas `nofile`, karena setiap koneksi email bersamaan dan akses kotak surat menghabiskan deskriptor file. Jika Anda menjalankan infrastruktur email sendiri daripada menggunakan Email Hosting yang dikelola, menyetel `nofile` ke setidaknya 65536 untuk pengguna email adalah hal yang tidak dapat ditawar pada server dengan beban sedang mana pun.
Matriks Keputusan: Apa yang Dikonfigurasi dan Di Mana
| Skenario | Metode yang Direkomendasikan | Parameter Utama |
|---|---|---|
| — | — | — |
| Sesi pengguna interaktif | `/etc/security/limits.conf` | `nofile`, `nproc`, `core` |
| Layanan yang dikelola systemd | Bagian `[Service]` unit systemd | `LimitNOFILE`, `LimitNPROC`, `LimitCORE` |
| Container Docker | Flag `–ulimit` atau `daemon.json` | `nofile`, `nproc` |
| Pengujian shell satu kali | Perintah `ulimit` langsung | Flag apa pun |
| Server bersama multi-tenant | `limits.conf` + penegakan PAM | `nproc`, `nofile`, `fsize`, `cpu` |
| Pod Kubernetes | Konteks keamanan pod + cgroups | Dikelola oleh kubelet |
| Penyesuaian khusus aplikasi | File drop-in `limits.d/` | Parameter khusus layanan |
Daftar Periksa Poin Penting Teknis
- Selalu verifikasi batas yang diterapkan melalui `/proc/<PID>/limits`, bukan `ulimit -a` tingkat shell, untuk layanan yang berjalan.
- Untuk layanan systemd, konfigurasikan batas dalam file unit menggunakan direktif `Limit*` — `limits.conf` tidak dibaca oleh systemd secara default.
- Tetapkan `nofile` minimal `65536` untuk layanan mana pun yang menangani koneksi jaringan; `131072` atau lebih tinggi untuk beban kerja dengan konkurensi tinggi.
- Jangan pernah menetapkan hard limit sama dengan soft limit kecuali Anda memiliki persyaratan keamanan tertentu — aplikasi memerlukan ruang untuk menyesuaikan diri sendiri.
- Nonaktifkan core dump (`LimitCORE=0`) di produksi; aktifkan dengan jalur yang terkontrol di staging.
- Batas `nproc` menghitung thread di Linux — pertimbangkan hal ini saat mengonfigurasi aplikasi JVM atau runtime Go.
- Sesuaikan `fs.file-max` melalui `sysctl` bersama dengan batas `nofile` per-proses untuk menghindari kelelahan `ENFILE` di seluruh sistem.
- Di lingkungan yang dikontainerisasi, batas kernel host adalah batas atas yang keras — pengaturan `ulimit` tingkat container tidak dapat melampauinya.
- Gunakan cgroups v2 untuk penegakan memori dan I/O; gunakan `ulimit` untuk batas deskriptor file, jumlah proses, dan kontrol core dump.
- Setelah perubahan batas apa pun di `limits.conf` atau file unit systemd, mulai ulang layanan yang terpengaruh dan verifikasi dengan `/proc/<PID>/limits`.
FAQ
Apakah `ulimit` berlaku untuk proses root?
Wildcard `*` di `/etc/security/limits.conf` secara eksplisit mengecualikan root. Proses root juga melewati penegakan hard limit untuk sebagian besar jenis sumber daya — root dapat menaikkan hard limit-nya sendiri. Untuk menerapkan batas pada root, tambahkan entri `root` eksplisit di `limits.conf`, meskipun banyak layanan sistem yang berjalan sebagai root akan mengabaikan batas yang diterapkan PAM jika dimulai di luar sesi login.
Mengapa perubahan `limits.conf` saya tidak berpengaruh pada layanan yang berjalan?
`limits.conf` diterapkan oleh PAM pada waktu login. Layanan yang dimulai oleh systemd, SysVinit, atau Upstart tidak melalui PAM dan oleh karena itu tidak mewarisi pengaturan `limits.conf`. Konfigurasikan batas langsung dalam file unit systemd menggunakan `LimitNOFILE` dan direktif terkait, kemudian jalankan `systemctl daemon-reload && systemctl restart <service>`.
Berapa nilai maksimum yang dapat saya tetapkan untuk `nofile`?
Maksimum per-proses dibatasi oleh parameter kernel `fs.nr_open` (default: 1.048.576 pada sebagian besar kernel). Total seluruh sistem dibatasi oleh `fs.file-max`. Anda dapat menaikkan `fs.nr_open` melalui `sysctl`, tetapi nilai di atas 1.048.576 memerlukan kompilasi ulang kernel pada kernel yang lebih lama. Secara praktis, 524.288 atau 1.048.576 mencakup hampir semua kasus penggunaan produksi.
Bagaimana cara memeriksa apakah suatu proses telah mencapai batas `ulimit`-nya?
Periksa log kernel dengan `dmesg | grep -i "ulimit|RLIMIT|too many open|cannot allocate"`. Log aplikasi biasanya akan menampilkan `EMFILE` (terlalu banyak file terbuka), `ENOMEM` (kegagalan alokasi memori), atau `EAGAIN` (sumber daya sementara tidak tersedia). Silang referensikan dengan `/proc/<PID>/limits` dan jumlah deskriptor saat ini melalui `ls /proc/<PID>/fd | wc -l`.
Apakah `ulimit` cukup untuk isolasi sumber daya di lingkungan multi-tenant?
Tidak. `ulimit` menyediakan penjaga per-proses dan per-pengguna tetapi tidak memberlakukan batas bandwidth memori, disk I/O, atau throughput jaringan. Untuk isolasi multi-tenant yang sesungguhnya, kombinasikan `ulimit` dengan pengontrol sumber daya cgroups v2, dan pertimbangkan isolasi namespace (namespace pengguna, namespace PID) untuk batas keamanan yang lebih kuat. Pada infrastruktur yang dikelola, kontrol ini biasanya berlapis di tingkat hypervisor dan runtime container.
