VPS dikerahkan dengan runbook empat langkah, stack dirawat dengan kalender tri-ritme, dan toko melayani pelanggan lewat HTTPS dengan log yang bisa dijahit ujung ke ujung. Namun “semua bab sudah dibaca” dan “produksi sudah aman” adalah dua kalimat yang berbeda jaraknya sejauh langit dan tanah — yang pertama perasaan, yang kedua harus bisa dibuktikan kepada orang lain: rekan tim baru, auditor, bahkan diri kita sendiri enam bulan lagi saat lupa detail. Chapter penutup section ini mengubah seluruh keputusan sepanjang perjalanan menjadi satu checklist tujuh kategori yang tiap barisnya bisa dieksekusi dari terminal, lengkap dengan cara memakainya saat go-live dan saat audit bulanan. Bentuknya sengaja dibuat kasar dan bisa dicetak: kolom item, kolom alasan satu kalimat, dan kolom rujukan chapter tempat keputusan itu lahir — karena checklist yang tidak menjelaskan dirinya hanya melatih kepatuhan, bukan pemahaman.
Tujuh Kategori Checklist #
Image #
| Item | Mengapa | Rujukan |
|---|---|---|
| Image dibangun multi-stage dan ramping | toolchain build tidak ikut terbang ke produksi — ukuran adalah permukaan serangan | Multi-Stage Build, Persiapan Production Image |
| Deployment menunjuk tag eksplisit; rilis kritis menunjuk digest | tag bisa ditimpa siapa pun yang berhak push, digest tidak pernah berubah | Image Digest, Strategi Versioning Image |
| Aplikasi berjalan sebagai USER non-root | proses UID 0 di dalam container adalah kegagalan desain yang menunggu satu eksploitasi | Menjalankan Container Non-Root, Workdir dan User |
| Base image minimal — tanpa shell bila aplikasi mengizinkan | penyerang tanpa shell tidak punya ruang jelajah setelah masuk | Mengurangi Ukuran Image, Persiapan Production Image |
| Scan image lolos ambang — tanpa critical/high | image mewarisi CVE dependensinya; lubang yang diketahui adalah pintu termurah | Image Scanning, Image Provenance |
Container #
| Item | Mengapa | Rujukan |
|---|---|---|
| Root filesystem read-only dengan tmpfs untuk state kecil | aplikasi yang tidak bisa menulis berkas tak bisa ditulisi balik | Read-Only Filesystem |
| Capabilities di-drop semaksimal yang aplikasi izinkan | dua belas kapabilitas bawaan jauh lebih banyak dari kebutuhan aplikasi web | Linux Capabilities |
| Tidak ada satu pun container berjalan –privileged | privileged melepas seccomp, seluruh device host, dan semua pagar kernel sekaligus | Privileged Container |
| Resource limit CPU dan memory terpasang di seluruh service | tetangga yang rakus dan OOM killer tidak boleh menghakimi seluruh host | CPU dan Memory Limit, Resource di Compose |
| pids-limit mengikat percabangan proses | fork bomb mati di dalam container, bukan di lantai mesin | PIDs Limit dan Ulimit |
Jaringan #
| Item | Mengapa | Rujukan |
|---|---|---|
| Hanya proxy yang menerbitkan port host (80/443) | satu pintu publik berarti satu permukaan yang diaudit dan satu sertifikat yang dirawat | Reverse Proxy dan TLS, Port Mapping |
| Pemetaan debug internal terikat 127.0.0.1 | loopback tidak menembus ufw, docker-proxy, maupun internet | Port Publishing, Docker di VPS |
| Stack hidup di custom network dan memanggil nama service | DNS embedded menjawab dan stack terisolasi dari tetangga di bridge default | Membuat Custom Network, Container DNS |
Data #
| Item | Mengapa | Rujukan |
|---|---|---|
Volume produksi berlabel env=prod dan dideklarasikan external | dikelola oleh operator, selamat dari down -v maupun prune yang salah waktu | Persistent Storage Production |
| Backup terbukti bisa dipulihkan pada drill terakhir | backup yang tak teruji adalah harapan dengan nama berkas | Backup dan Recovery |
| DRP tertulis dengan angka RTO dan RPO | musibah dikerjakan dari lembar runbook, bukan dari improvisasi panik | Backup dan Recovery, Backup dan Restore Volume |
| Retensi berjalan: staging tujuh hari, off-site lebih panjang | arsip tanpa rotasi hanya memindahkan musibah dari “data hilang” ke “disk penuh” | Backup dan Restore Volume, Backup Database |
Log #
| Item | Mengapa | Rujukan |
|---|---|---|
| Driver log dengan rotasi terpasang di daemon.json | log tanpa batas adalah bom waktu yang meledak pelan di disk | Logging Production, Konfigurasi Docker Engine |
| Log aplikasi terstruktur: satu event satu baris JSON | baris yang bisa diurai mesin bisa disaring tanpa menebak-tebak | Docker Logs |
| Korelasi lintas service: nama container, NTP sinkron, trace id | satu permintaan bisa dijahit dari proxy sampai db dalam satu grep | Logging Production |
Keamanan #
| Item | Mengapa | Rujukan |
|---|---|---|
| Rahasia hidup di compose secrets atau vault, bukan environment polos | docker inspect menampilkan environment apa adanya kepada siapa pun yang bisa membacanya | Secrets dan Environment, Secret Management |
| Rotasi rahasia terjadwal dan tercatat tanggalnya | kata sandi berumur panjang adalah kunci pintu yang tak pernah diganti | Secrets dan Environment |
| Socket Docker tidak di-mount ke container mana pun | socket yang terpasang adalah akar host yang dipinjamkan tanpa audit | Docker Socket Security |
| Engine dan base image diperbarui lewat jadwal rilis yang disiplin | lubang yang sudah diketahui publik paling sering dipakai justru karena tak pernah ditutup | Docker di VPS, Image Scanning |
Operasional #
| Item | Mengapa | Rujukan |
|---|---|---|
| Healthcheck tiga lapis: di image, diinjak compose, digerbangi depends_on | “proses hidup” dan “siap melayani” adalah dua kalimat yang berbeda | Healthcheck dan Restart Policy, HEALTHCHECK |
Restart policy unless-stopped pada seluruh service | restart daemon dan reboot server tidak boleh menjadi pemadaman manual | Restart Policy, Docker di VPS |
| Runbook rilis dan rollback tertulis dan pernah dilatih | tujuh langkah dipegang saat kepala tenang, bukan disusun saat darurat | Update dan Rollback |
| RTO dan RPO diambil dari durasi drill sungguhan | angka yang diukur bisa dipercaya; angka yang dikira menagih bunga | Backup dan Recovery |
Cara Memakai Checklist #
Status tiap baris hanya mengenal dua nilai: ✅ berarti berlaku dan punya bukti, ⬜ berarti belum atau tidak lagi — tidak ada kolom “hampir”, karena “hampir” adalah ⬜ yang berusaha menyelamatkan muka. Dua momen pemakaian mengatur ritmenya. Saat go-live: seluruh dua puluh delapan baris harus ✅ tanpa kecuali; satu ⬜ pada kategori Keamanan atau Data bukan catatan kaki melainkan penundaan — toko tidak boleh melayani pelanggan dengan pintu yang diketahui terbuka. Saat audit bulanan: cetak checklist, jalankan perintah audit pada bagian berikutnya, tandai hasilnya, dan simpan lembar hasilnya sebagai berkas bukti; item yang bertahan ⬜ selama dua audit berturut-turut naik kelas dari catatan menjadi pekerjaan berjadwal. Checklist yang ditandai tanpa menjalankan audit terminal adalah dongeng — dan bagian berikutnya memastikan kita tidak bercerita.
Audit Cepat dari Terminal #
Lima perintah membaca keadaan yang sungguhan, bukan keadaan yang dideklarasikan. Perintah pertama menguji tiga kebijakan container sekaligus pada seluruh stack:
$ cd /srv/toko
$ docker inspect --format '{{.Name}} restart={{.HostConfig.RestartPolicy.Name}} read_only={{.HostConfig.ReadonlyRootfs}} cap_drop={{.HostConfig.CapDrop}}' $(docker compose ps -q)Output yang Dihasilkan:
/toko-proxy-1 restart=unless-stopped read_only=false cap_drop=[NET_RAW]
/toko-frontend-1 restart=unless-stopped read_only=true cap_drop=[NET_RAW]
/toko-backend-1 restart=unless-stopped read_only=true cap_drop=[NET_RAW]
/toko-db-1 restart=unless-stopped read_only=false cap_drop=[NET_RAW]
/toko-cache-1 restart=unless-stopped read_only=false cap_drop=[NET_RAW]Lima baris itu memberi nilai cepat: restart=unless-stopped seragam ✅, cap_drop=[NET_RAW] seragam ✅, sedangkan read_only membagi dua — frontend dan backend sudah bersih, sementara proxy, db, dan cache masih false. Ketiganya punya alasan historis (nginx menulis berkas pid, postgres menulis PGDATA, redis menulis dump), dan ketiganya juga punya solusi yang sama: tmpfs untuk path kecil sebagaimana resep Chapter Read-Only Filesystem — itulah cara kerja audit yang sehat: menemukan lubang yang sah untuk ditemukan. Perintah kedua membaca permukaan jaringan dari sudut pandang daemon:
$ for c in $(docker compose ps -q); do docker inspect --format '{{.Name}} {{json .HostConfig.PortBindings}}' "$c"; doneOutput yang Dihasilkan:
/toko-proxy-1 {"443/tcp":[{"HostIp":"","HostPort":"443"}],"80/tcp":[{"HostIp":"","HostPort":"80"}]}
/toko-frontend-1 {}
/toko-backend-1 {}
/toko-db-1 {"5432/tcp":[{"HostIp":"127.0.0.1","HostPort":"5432"}]}
/toko-cache-1 {}Baris proxy menunjukkan HostIp kosong — berarti terikat 0.0.0.0 dan memang disengaja untuk pintu publik; baris db menunjukkan pemetaan debug yang terkurung di 127.0.0.1 — lolos aturan kategori Jaringan, dengan catatan pencabutan bila pemetaan itu tak lagi dibutuhkan; tiga sisanya {} — murni internal. Bila suatu hari baris baru muncul dengan HostIp kosong pada service selain proxy, itulah alarm yang pertama. Perintah ketiga menagih janji kategori Image:
$ docker scout cves registry.example.com/toko/backend:1.5.0Output yang Dihasilkan (dipotong):
i registry.example.com/toko/backend:1.5.0
0C 1H 4M 12L
...
ID Severity Package Version Fixed in
CVE-2026-3928 High npm/semver 5.7.1 5.7.2
...Ringkasan 0C 1H 4M 12L berarti nol critical, satu high, empat medium, dua belas low — dan baris high itu menjatuhkan nilai item “scan lolos ambang” menjadi ⬜ sampai dependensi dinaikkan ke 5.7.2 dan image di-rebuild; audit mencatatnya sebagai temuan, bukan mengkalimkannya sebagai kelulusan. Dua perintah terakhir menutup lingkaran data dan disk:
$ docker system df
$ docker volume ls --filter label=env=prodOutput yang Dihasilkan:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 6 5 1011MB 142MB (14%)
Containers 5 5 36.7MB 0B (0%)
Local Volumes 2 2 489MB 0B (0%)
Build Cache 0 0 0B 0B
DRIVER VOLUME NAME
local toko_db-data
local toko_uploadsEnam image dengan lima aktif — satu generasi cadangan untuk rollback, rencana yang justru sehat; dua volume berlabel menjawab item kategori Data; dan RECLAIMABLE 142MB adalah generasi 1.3.0 yang menunggu kebijakan retensi, bukan panic prune.
#!/usr/bin/env bash
set -euo pipefail
cd /srv/toko
echo "== kebijakan container =="
docker inspect --format '{{.Name}} restart={{.HostConfig.RestartPolicy.Name}} read_only={{.HostConfig.ReadonlyRootfs}} cap_drop={{.HostConfig.CapDrop}}' $(docker compose ps -q)
echo "== port yang diterbitkan =="
for c in $(docker compose ps -q); do
docker inspect --format '{{.Name}} {{json .HostConfig.PortBindings}}' "$c"
done
echo "== konsumsi disk =="
docker system df
echo "== volume produksi =="
docker volume ls --filter label=env=prod
echo "== ringkasan kerentanan =="
docker scout cves registry.example.com/toko/backend:1.5.0 | head -n 4Skrip audit toko pada Jumat terakhir bulan Oktober menghasilkan tiga temuan yang semuanya jujur: tiga rootfs masih writable dengan rencana tmpfs, satu high pada semver dengan rencana rebuild, dan pemetaan debug 5432 yang menunggu keputusan pencabutan. Tidak satu pun menutup go-live — semuanya masuk daftar pekerjaan berjadwal — dan itulah hasil akhir yang benar dari sebuah audit: bukan pernyataan kesempurnaan, melainkan daftar yang bisa dikerjakan.
Produksi toko kini berdiri di atas fondasi yang bisa dipertanggungjawabkan baris demi baris — image yang ramping dan teridentifikasi digest, container yang dipagari kernel, jaringan yang bermuara ke satu pintu TLS, data yang punya tiga salinan dan runbook pulang, log yang terukur dan terkorelasi, dan ritual operasional yang tertulis serta terlatih. Namun perhatikan bentuk dari seluruh kerja ini: build, scan, tag, catat digest, deploy, verifikasi, audit — urutan langkah yang sama, setiap kali, tanpa improvisasi. Urutan yang teratur seperti itu adalah pekerjaan yang paling layak diserahkan kepada mesin, dan penyerahan itulah agenda section berikutnya: CI/CD dan Automation, dibuka dari Chapter Konsep Container di CI/CD, tempat seluruh ritual manual section ini berubah menjadi pipeline yang berjalan sendiri setiap commit.
Referensi #
- Docker Engine security — kapabilitas bawaan, keamanan default container, dan risiko socket daemon.
- docker container inspect — format template untuk audit kebijakan dan port bindings.
- Docker Scout — pemindaian kerentanan image beserta ringkasan tingkatannya.
- docker system df — konsumsi disk image, container, dan volume untuk audit bulanan.