Docker di VPS: Apa yang Sebenarnya Berubah?
Kenali 4 masalah Docker di VPS: RAM habis, port publik melewati UFW, container tidak aktif setelah reboot, dan disk penuh oleh image serta cache.
Perubahan saat menjalankan Docker pada VPS
Docker pada VPS menggunakan engine dan image yang sama seperti Docker pada laptop, sehingga semua perintah yang sudah Anda kenal tetap berfungsi. Yang berubah adalah kapasitas sistem di sekitarnya. Laptop memiliki memori cadangan, firewall yang jarang dipindai, dan disk yang cukup besar sehingga Anda tidak perlu memantaunya. Server sewaan memiliki batas memori tetap, alamat IP publik yang mulai dipindai dalam hitungan menit setelah boot, dan root filesystem yang akan dipenuhi Docker tanpa meminta konfirmasi.
Empat perbedaan berikut paling sering menyebabkan masalah pada server kecil:
- Memori terbatas, dan kernel mengatasi kekurangan memori dengan menghentikan proses.
- Port yang dipublikasikan melewati UFW (uncomplicated firewall) secara langsung karena Docker menulis aturan firewall-nya sendiri.
- Container tidak berjalan kembali setelah reboot kecuali Anda telah mengaturnya sebelumnya.
- Image, container, volume, dan cache build terus bertambah hingga disk penuh.
Setiap bagian di bawah ini menjelaskan kegagalan, string yang benar-benar akan Anda lihat, dan panduan yang membahas perbaikannya secara mendalam. Jika Anda belum menulis file compose, baca Dasar-dasar Docker Compose pada VPS terlebih dahulu, lalu kembali ke sini. Halaman ini mengasumsikan Anda sudah dapat menjalankan sebuah stack.
Berapa banyak RAM yang digunakan container Docker?
Lebih sedikit daripada yang diperkirakan kebanyakan orang. Container adalah proses dalam cgroup (control group), bukan mesin virtual, sehingga tidak memiliki kernel guest dan tidak mendapat alokasi tetap. Penggunaannya bergantung pada data yang diakses oleh proses di dalamnya. Karena itu, seluruh stack dapat berjalan dalam 2 GB, sedangkan stack yang sama jika dibangun dari mesin virtual tidak akan muat.
Angka di bawah ini adalah angka idle umum untuk image standar pada Ubuntu 24.04 dengan konfigurasi default, yang dibaca dari docker stats beberapa menit setelah start. Gunakan angka ini sebagai dasar perencanaan, bukan sebagai benchmark untuk workload Anda. Jalankan docker stats --no-stream pada server Anda sendiri sebelum mempercayai angka apa pun, termasuk angka berikut.
The data behind this chart
[
{
"label": "nginx",
"idle_mb": 8,
"budget_mb": 64
},
{
"label": "Redis 7",
"idle_mb": 12,
"budget_mb": 128
},
{
"label": "Traefik v3",
"idle_mb": 40,
"budget_mb": 128
},
{
"label": "PostgreSQL 16",
"idle_mb": 45,
"budget_mb": 512
},
{
"label": "Uptime Kuma",
"idle_mb": 95,
"budget_mb": 256
},
{
"label": "MariaDB 11",
"idle_mb": 190,
"budget_mb": 512
},
{
"label": "Nextcloud (Apache)",
"idle_mb": 210,
"budget_mb": 768
}
]Kedua kolom tersebut memiliki fungsi yang berbeda. idle_mb adalah penggunaan container saat tidak melakukan apa pun. budget_mb adalah jumlah yang perlu disediakan saat merencanakan kapasitas, karena penggunaan nyata tidak berada dalam kondisi idle. PostgreSQL menggunakan hampir 45 MB saat idle dan memerlukan 512 MB setelah koneksi, proses sort, dan cache aktif. Gunakan kolom anggaran untuk perencanaan. Gunakan kolom idle untuk debugging.
Perhatikan pola dari 7 baris tersebut. nginx menggunakan 8 MB saat idle, sedangkan Nextcloud menggunakan 210 MB. Proxy di depan aplikasi Anda hampir tidak menggunakan resource. Database dan aplikasi PHP adalah komponen yang menentukan kapasitas server.
Perhatikan satu hal tentang docker stats: angka penggunaan memori mencakup page cache yang dimuat oleh pembacaan file dari container itu sendiri, sehingga angkanya naik selama beberapa saat setelah start lalu stabil. Pantau selama satu jam sebelum menyimpulkan bahwa terjadi kebocoran memori.
Menentukan ukuran VPS: apa yang muat dalam 2 GB, 4 GB, dan 8 GB
Kurangi terlebih dahulu porsi yang digunakan host. Kernel, systemd, journald, sshd, dan Docker daemon menggunakan RAM yang sama dengan container Anda, dan dockerd bersama containerd menggunakan sekitar 100 MB. Anda juga memerlukan memori kosong untuk page cache dan lonjakan penggunaan saat image build atau database dump berjalan.
The data behind this chart
[
{
"plan": "2 GB VPS",
"total_mb": 2048,
"host_reserve_mb": 768,
"container_mb": 1280
},
{
"plan": "4 GB VPS",
"total_mb": 4096,
"host_reserve_mb": 1024,
"container_mb": 3072
},
{
"plan": "8 GB VPS",
"total_mb": 8192,
"host_reserve_mb": 1536,
"container_mb": 6656
}
]host_reserve_mb mencakup sistem operasi, Docker daemon, dan ruang cadangan agar server tetap responsif saat mengalami beban. Sisa memori adalah container_mb, dan hanya jumlah inilah yang dapat Anda alokasikan. Cadangan tersebut meningkat sesuai paket, dari 768 MB pada server terkecil hingga 1536 MB pada server terbesar, karena server yang lebih besar menjalankan lebih banyak container, menulis lebih banyak log, dan memerlukan lebih banyak page cache.
Paket 2 GB menyisakan 1280 MB untuk container. Gunakan 512 MB untuk PostgreSQL dan 128 MB untuk Traefik, maka setengah anggaran tersebut sudah terpakai. Sisanya cukup untuk dua aplikasi kecil, masing-masing sekitar 256 MB. Ini adalah server yang nyata dan berguna. Namun, ruang tersebut tidak cukup untuk menjalankan Nextcloud dan search cluster sekaligus.
Paket 4 GB menyisakan 3072 MB. Jumlah ini cukup untuk menjalankan database, reverse proxy, tiga aplikasi, dan satu container monitoring secara bersamaan. Ini adalah ukuran terkecil yang layak digunakan untuk layanan penting, karena memori cadangan dapat menyerap dampak deploy yang bermasalah.
Paket 8 GB menyisakan 6656 MB dari total 8192 MB, dan batasnya biasanya bergeser dari memori ke CPU atau throughput disk. Salah satu jenis container menentukan ukuran dari konfigurasinya, bukan dari beban: local model server mencadangkan KV cache secara proporsional terhadap context window-nya, sehingga menaikkan num_ctx Ollama dapat menambah gigabyte ke anggaran sebelum satu pun request diterima. Jika perhitungan menunjukkan bahwa stack Anda tidak muat, pilih paket yang lebih besar daripada melakukan tuning untuk mengatasinya: biaya VPS yang sebenarnya menjelaskan nilai gigabyte tambahan tersebut per bulan.
Dua aturan membuat perhitungan tetap akurat. Tetapkan memory limit pada setiap service agar satu proses yang tidak terkendali tidak menghabiskan seluruh memori server. Sisakan pula bagian teratas anggaran, karena docker compose build dan pg_dump sama-sama memerlukan memori pada saat yang paling buruk. Memory limit di Docker Compose menjelaskan sintaks dan jebakannya.
Mengapa container saya keluar dengan kode 137?
Karena kernel menghentikannya. 137 adalah 128 ditambah 9, dan signal 9 adalah SIGKILL. Container meminta memori melebihi batas yang diizinkan, lalu OOM killer menghentikannya.
CONTAINER ID IMAGE STATUS
9f2c1a4d7b3e postgres:16 Exited (137) 4 minutes agoKonfirmasikan penyebabnya, jangan menebak:
docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory""OOMKilled": true berarti container mencapai batas cgroup-nya sendiri, dan log kernel menyebutkan proses yang dipilihnya:
Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kBIni adalah kondisi yang baik karena dampaknya terbatas pada satu container. Kondisi buruk terjadi jika container tidak memiliki batas sama sekali. Tanpa batas, kapasitas maksimumnya adalah seluruh mesin. Kebocoran memori pada satu service dapat menguras resource host, lalu kernel memilih korban berdasarkan ukuran proses di seluruh sistem. Baris log tidak lagi memiliki prefiks Memory cgroup dan berbunyi Out of memory: Killed process 2417 (postgres). Proses yang dipilih sering kali adalah database Anda, sedangkan container yang mengalami kebocoran tetap berjalan. Karena itu, menetapkan batas pada setiap service lebih penting daripada menentukan nilai yang tepat untuk satu batas tertentu.
Swap mengubah waktu terjadinya masalah, bukan perhitungannya. Sebagian besar image VPS dikirim tanpa swap. Periksa dengan swapon --show. Perintah ini tidak menampilkan apa pun jika swap tidak tersedia. File swap memberi kernel tempat untuk memindahkan page yang tidak aktif, sehingga Anda memiliki waktu beberapa menit untuk menyadari adanya masalah.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h sekarang seharusnya menampilkan total non-zero pada baris Swap. Swap tidak menambah RAM. Mesin yang terus-menerus mengalami tekanan memori dapat menjadi sangat lambat sehingga Anda tidak dapat masuk melalui SSH untuk memperbaikinya. Karena itu, perlakukan swap sebagai buffer peringatan dan perbaiki penentuan kapasitasnya.
Mengapa UFW tidak memblokir port Docker yang dipublikasikan?
Karena trafik tersebut tidak pernah mencapai chain yang dijaga UFW. Saat Anda memublikasikan port dengan -p 5432:5432 atau entri compose ports:, daemon menulis aturan DNAT (destination network address translation) ke tabel nat dan aturan accept ke chain DOCKER miliknya sendiri. Paket yang ditujukan ke container diteruskan ke container tersebut, bukan dikirim ke host. Karena itu, paket diproses melalui jalur FORWARD dan tidak pernah melewati aturan INPUT yang ditulis UFW.
Anda dapat mengamati proses ini di server:
sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432UFW dapat menampilkan 5432 DENY IN Anywhere, sementara tabel nat memiliki aturan DNAT tcp ... to:172.18.0.2:5432 untuk port yang sama. Dari mesin lain, nc -vz your.server.ip 5432 tetap dapat terhubung. Database tersebut berada di Internet publik, meskipun firewall menyatakan sebaliknya.
Solusinya adalah mengurangi port yang dipublikasikan. Container dalam satu project compose berbagi jaringan dan dapat saling mengakses berdasarkan nama service. Karena itu, database yang hanya melayani aplikasi di sebelahnya tidak memerlukan entri ports: sama sekali. Jika Anda memerlukan akses lokal, bind publikasi tersebut ke loopback:
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"Setelah docker compose up -d, nc -vz your.server.ip 5432 dari luar akan gagal, sedangkan psql -h 127.0.0.1 -p 5432 pada host tetap berfungsi. Dalam stack kecil yang dikonfigurasi dengan baik, hanya reverse proxy yang memublikasikan port, yaitu 80 dan 443. Mengapa port Docker yang dipublikasikan melewati UFW membahas chain DOCKER-USER untuk kasus ketika Anda harus memublikasikan port tetapi tetap ingin memfilternya, sedangkan Dasar-dasar firewall UFW membahas aturan host yang mendasarinya.
Mengapa container saya hilang setelah reboot?
Karena tidak ada konfigurasi yang memerintahkannya untuk berjalan kembali. Container dibuat dengan restart policy no jika Anda tidak menetapkan policy lain. Karena itu, reboot membuatnya tetap berhenti dan daemon tidak akan menanganinya. Reboot bukan kejadian yang jarang pada VPS: pembaruan kernel dari unattended upgrades, pemeliharaan oleh provider, dan rangkaian OOM di atas semuanya dapat menyebabkan reboot.
Dua hal harus terpenuhi. Daemon harus berjalan saat boot:
systemctl is-enabled dockerPerintah tersebut menampilkan enabled pada instalasi Ubuntu standar. Selanjutnya, setiap service memerlukan policy:
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped menjalankan kembali container setelah reboot dan tetap menghormati container yang Anda hentikan secara sengaja. always juga menjalankan kembali container yang Anda hentikan secara sengaja setiap kali daemon dimulai ulang. Hal ini dapat mengejutkan saat proses debugging. Mengedit file saja tidak cukup karena restart policy ditetapkan saat container dibuat. Jalankan docker compose up -d agar container dibuat ulang, lalu periksa nilai yang sedang digunakan:
docker inspect my-app | grep -A3 RestartPolicySelanjutnya, lakukan reboot pada server secara sengaja dan jalankan docker compose ps di direktori project. Stack yang tetap berjalan setelah reboot terencana juga akan tetap berjalan setelah reboot yang tidak terencana. Jika stack Anda memerlukan jaminan urutan atau job sekali jalan saat boot, unit systemd adalah tool yang lebih tepat: menjalankan Docker Compose saat boot berisi unit file-nya. Untuk memastikan bahwa container yang berjalan kembali benar-benar melayani request, tambahkan healthcheck Compose.
Mengapa disk VPS saya penuh?
Karena Docker menyimpan semuanya sampai Anda memerintahkannya untuk menghapusnya. Setiap tag image yang pernah Anda pull, setiap container yang dihentikan, setiap volume anonim yang tertinggal setelah recreate, dan setiap layer build cache tetap tersimpan di disk. Pada root filesystem berukuran 40 GB atau 80 GB, yang umum untuk ukuran paket ini, kondisi tersebut dapat menyebabkan outage dalam hitungan bulan, bukan tahun.
Disk penuh tidak terlihat seperti crash. Anda akan menerima no space left on device dari container, dari apt, dari journald, dan dari docker pull dalam jam yang sama. PostgreSQL berhenti menerima operasi tulis. Server tetap aktif, sehingga masalah ini lebih sulit diketahui daripada reboot loop.
Periksa sebelum menghapus:
docker system df
df -h /docker system df membagi total penggunaan menjadi image, container, local volume, dan build cache, serta menampilkan kolom RECLAIMABLE di samping masing-masing kategori. Pada server yang membuat image sendiri, build cache biasanya menjadi penggunaan terbesar.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a menghapus setiap image yang tidak digunakan oleh container mana pun. docker builder prune menghapus build cache. Keduanya aman dijalankan saat service aktif karena item yang sedang digunakan akan dilewati. Yang tidak aman adalah docker system prune --volumes, yang menghapus setiap volume yang saat ini tidak direferensikan oleh container mana pun. Stack yang Anda hentikan selama akhir pekan memiliki kondisi tersebut, dan volume databasenya ikut terhapus. Baca bind mounts versus named volumes sebelum menggunakan flag tersebut, dan buat backup terlebih dahulu.
Log container bertambah secara lebih tersembunyi. Driver default json-file tidak memiliki batas ukuran, sehingga satu container yang banyak menulis log dapat mengisi gigabyte di /var/lib/docker/containers. Tetapkan batas untuk setiap container di /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Terapkan perubahan dengan sudo systemctl restart docker. Perintah ini akan me-restart container Anda, jadi pilih waktu yang tepat. Batas tersebut hanya berlaku untuk container yang dibuat setelah perubahan. Karena itu, recreate container yang sedang berjalan dengan docker compose up -d --force-recreate, lalu konfirmasikan:
docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containersOutput inspect harus menunjukkan max-size telah ditetapkan. Jika nilainya kosong, container tersebut dibuat sebelum perubahan dan masih menulis tanpa batas.
Kebiasaan untuk menjaga Docker box kecil tetap sehat
Semua ini tidak memerlukan dashboard atau tool yang harus Anda pelajari.
- Jalankan
docker system dfdandf -h /pada tanggal pertama setiap bulan. Dua perintah, tiga puluh detik, dan Anda dapat melihat trennya jauh sebelum menjadi gangguan layanan. - Tetapkan batas memori untuk setiap service, termasuk service yang Anda yakini berukuran kecil. Batas ini mengubah gangguan pada seluruh host menjadi hanya satu container yang perlu dimulai ulang.
- Pantau box dari lokasi lain agar Anda mengetahui tekanan memori atau disk sebelum kernel bertindak. Uptime Kuma berjalan dalam container dan menggunakan sekitar 95 MB saat idle.
- Cadangkan volume, bukan container. Container dapat dibuang, sedangkan volume tidak. pencadangan restic pada VPS mencakup jadwal dan pengujian pemulihan.
- Tetapkan tag image dalam file compose dan perbarui pada hari yang Anda tentukan. Dengan
latest, versi yang Anda dapatkan daridocker compose pullberikutnya adalah versi yang dirilis pada pagi itu.
VPS kecil yang menjalankan Docker dapat tetap sehat selama bertahun-tahun jika empat angka tetap dalam batas yang wajar: anggaran memori, daftar port yang dipublikasikan, kebijakan restart setiap service, dan ruang disk kosong. Selain itu, semuanya sama dengan Docker yang sudah Anda jalankan di rumah.
FAQ
Berapa RAM yang saya perlukan untuk menjalankan Docker pada VPS?
Docker sendiri tidak membutuhkan banyak resource. Daemon dan containerd secara keseluruhan menggunakan sekitar 100 MB, sedangkan kebutuhan lainnya ditentukan oleh container Anda. Alokasikan terlebih dahulu resource untuk host: 768 MB pada mesin 2048 MB untuk sistem operasi, daemon, dan ruang cadangan. Dengan demikian, tersedia 1280 MB untuk container. Database sebesar 512 MB, reverse proxy sebesar 128 MB, dan dua aplikasi kecil dapat berjalan dalam alokasi tersebut. Ukur stack Anda sendiri dengan docker stats --no-stream, bukan dengan mengandalkan angka yang dipublikasikan.
Apakah saya dapat menjalankan Docker pada VPS 1 GB?
Ya, untuk satu atau dua container ringan. Buat file swap sebelum memulainya. Sekitar setengah kapasitas mesin 1 GB akan terpakai setelah sistem operasi dan daemon Docker berjalan. Sisa kapasitasnya cukup untuk satu aplikasi kecil dan reverse proxy, tetapi tidak cukup untuk database dengan beban nyata. Pembuatan image pada mesin dengan kapasitas tersebut dapat gagal atau menghentikan proses lain. Karena itu, buat image di mesin lain lalu pull image yang sudah selesai.
Apakah UFW melindungi container Docker?
Tidak untuk port yang Anda publish. Docker menulis aturan DNAT dan forward sendiri. Akibatnya, paket yang ditujukan ke port container yang dipublish akan diteruskan ke container, bukan dikirim ke host. Aturan INPUT yang dikelola UFW tidak pernah memproses paket tersebut. ufw deny 5432 dapat tetap aktif ketika port itu merespons dari Internet. Gunakan 127.0.0.1:5432:5432 untuk melakukan publish ke loopback, jangan publish service internal, atau lakukan filtering pada chain DOCKER-USER.
Apakah container saya akan restart setelah VPS reboot?
Hanya jika container dibuat dengan restart policy. Tetapkan restart: unless-stopped pada setiap service, jalankan docker compose up -d agar container dibuat ulang dengan policy tersebut, lalu pastikan systemctl is-enabled docker menampilkan enabled. Setelah itu, lakukan reboot secara sengaja dan periksa docker compose ps. Restart policy yang belum pernah diuji belum dapat dianggap sebagai restart policy yang berfungsi.
Seberapa sering saya harus melakukan prune image Docker?
Sebulan sekali sudah cukup untuk sebagian besar server kecil, atau setiap kali docker system df melaporkan ruang yang dapat dipulihkan dan Anda membutuhkannya. docker image prune -a dan docker builder prune aman dijalankan saat service aktif, karena image dan cache yang sedang digunakan akan dilewati. Hindari docker system prune --volumes kecuali Anda mengetahui dengan pasti volume mana yang tidak memiliki referensi, karena perintah tersebut menghapus data dari stack yang kebetulan sedang berhenti.