Command override memberi kita kuasa mengubah apa yang dijalankan container untuk satu kali jalan, tetapi kebutuhan yang lebih sering muncul justru lebih halus: aplikasi yang sama harus berperilaku berbeda di lingkungan yang berbeda. Budi ingin menguji myapp dalam mode production tanpa membangun ulang image, dan Siti perlu mengarahkan aplikasi yang sama ke basis data uji yang berganti-ganti dalam sepekan. Membangun satu image per lingkungan adalah jalan yang mahal — build ulang, tag baru, dan deretan image yang isinya hanya berbeda satu baris konfigurasi. Jawaban yang benar adalah menyuntikkan konfigurasi saat runtime lewat environment variable, dan docker run menyediakan tiga pintunya: flag -e yang boleh diulang, bentuk pass-through tanpa nilai, dan --env-file untuk daftar panjang.
Nilai Baru dengan Flag -e #
Flag -e menerima bentuk NAMA=nilai dan menyisipkan variabel tersebut ke environment container pada saat container diciptakan. Flag ini boleh — dan biasanya memang — diulang beberapa kali dalam satu perintah. Budi menjalankan myapp dengan dua variabel sekaligus:
$ docker run -d --name myapp-prod -e NODE_ENV=production -e API_KEY=abc myapp:1.0Output yang Dihasilkan:
b1e4f8a2c9d6073e5a8c1b4d6f2a9e3c7b5d8f1a3c6e9b2d4f7a1c5e8b3d6f0aNilai variabel paling mudah diverifikasi dari sudut pandang dalam container, memakai docker exec yang kita kenal dari Chapter Menjalankan Perintah dalam Container:
$ docker exec myapp-prod printenv NODE_ENVOutput yang Dihasilkan:
production$ docker exec myapp-prod printenv API_KEYOutput yang Dihasilkan:
abcMari kita telaah tiga perintah di atas:
-e NODE_ENV=production -e API_KEY=abc— dua flag terpisah, dua variabel berbeda; daemon menyisipkan keduanya ke environment container sebelum proses utama dinyalakan.printenv NAMA— mencetak nilai satu variabel dari dalam container; nilaiproductiondanabcterbaca oleh proses tambahan hasil exec, bukti bahwa variabel benar-benar hidup di environment container.- Environment berlaku untuk semua proses container — proses utama hasil CMD atau ENTRYPOINT maupun proses tambahan hasil
docker execmelihat daftar variabel yang sama.
Daftar lengkap environment container juga bisa kita intip sekaligus:
$ docker exec myapp-prod printenvOutput yang Dihasilkan:
HOSTNAME=b1e4f8a2c9d6
NODE_ENV=production
API_KEY=abc
HOME=/root
NODE_VERSION=22.20.0
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
...Output di atas dipotong — beberapa baris bawaan image node lainnya tidak ditampilkan. Mari kita telaah asal-usul tiap barisnya:
NODE_ENVdanAPI_KEY— dua suntikan flag-emilik Budi; keduanya tidak ada di dalam image myapp:1.0 sama sekali.HOSTNAME=b1e4f8a2c9d6— diisi otomatis oleh daemon dengan ID pendek container; nilai ini berganti setiap kali container baru diciptakan.NODE_VERSIONdanPATH— bawaan imagenode:22-alpine, ditulis lewat instruksi ENV pada Dockerfile-nya dan diwarisi setiap container yang berdiri di atasnya; materi ENV dan ARG di Dockerfile sudah kita bedah pada Chapter ENV dan ARG.HOME=/root— direktori rumah user root, identitas bawaan myapp:1.0 yang pembahasannya menyusul pada Chapter User dan Permissions.
Jejak Environment pada docker inspect #
Seluruh environment container tersimpan pada konfigurasinya dan terbaca lewat docker inspect — teknik template --format yang akrab sejak Chapter Image Inspect dan History:
$ docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' myapp-prodOutput yang Dihasilkan:
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=22.20.0
...
NODE_ENV=production
API_KEY=abcBaris-baris bawaan image di tengah dipotong agar ringkas; dua baris terakhir adalah suntikan runtime. Nilai yang tersimpan di .Config.Env inilah yang diwarisi setiap proses di dalam container — dan format penyimpanannya polos, tanpa enkripsi apa pun.
Pass-Through dengan -e Tanpa Nilai #
Bentuk kedua flag -e hanya menyebut nama variabel tanpa tanda sama dengan: -e NAMA. Daemon kemudian menyalin nilai variabel dari shell host yang memanggil docker run ke dalam container:
$ export TOKEN_LATIHAN=tokendev123
$ docker run --rm -e TOKEN_LATIHAN myapp:1.0 printenv TOKEN_LATIHANOutput yang Dihasilkan:
tokendev123Mari kita telaah perilaku bentuk pass-through ini:
-e TOKEN_LATIHANtanpa=— daemon membaca nilaiTOKEN_LATIHANdari lingkungan shell yang mengetik perintah; container menerima variabel dengan nilai yang sama.- Variabel yang tidak ada dilewati — bila
TOKEN_LATIHANtidak diset di host, variabel itu sama sekali tidak disisipkan ke container; tidak ada galat, tidak ada nilai kosong. - Kegunaannya di skrip — skrip CI dapat menyalurkan token dari secrets manajer-nya tanpa pernah menuliskan nilai secara eksplisit di dalam berkas skrip; nilai mengalir dari lingkungan ke container.
Daftar Panjang dengan –env-file #
Ketika daftar variabel memanjang, mengetik deretan -e menjadi rawan salah ketik. Flag --env-file membacanya dari sebuah berkas teks. Budi membuat berkas .env di folder myapp — folder yang sama dengan Dockerfile-nya — dengan isi sebagai berikut:
$ cat .envOutput yang Dihasilkan:
NODE_ENV=production
API_KEY=abc
# level log untuk lingkungan uji
LOG_LEVEL=infoLalu container dijalankan dengan membaca daftar itu:
$ docker run --rm --env-file .env myapp:1.0 printenv NODE_ENV LOG_LEVELOutput yang Dihasilkan:
production
infoMari kita telaah format berkas dan hasilnya:
- Satu
KEY=VALUEper baris — setiap baris yang bukan komentar dan bukan baris kosong menjadi satu entri environment;printenvdengan dua argumen mencetak nilai keduanya, danAPI_KEYtidak tampil karena memang tidak diminta. - Baris berawalan
#adalah komentar — baris keterangan level log diabaikan begitu juga baris kosong; format ini sengaja sederhana, bukan parser shell. - Nilai dibaca mentah — tanda kutip yang ditulis menjadi bagian dari nilai, berbeda dengan kebiasaan beberapa tool lain yang mengupasnya; biasakan menulis nilai tanpa kutip.
- Berkas dibaca dari host, bukan dari image —
--env-filemenyasar berkas di direktori kerja shell yang menjalankandocker run; berkas.envtidak pernah ikut terbakar ke dalam image, dan bila kunci yang sama disebut oleh-esekaligus--env-file, nilai dari-eyang menang.
Runtime Menimpa ENV Image #
Hubungan -e dengan ENV bawaan image mengikuti satu aturan pendek: nilai runtime selalu menang. Image node:22-alpine membawa NODE_VERSION dari Dockerfile-nya, dan Budi menimpanya sekadar untuk membuktikan urutannya:
$ docker run --rm -e NODE_VERSION=ditimpa myapp:1.0 printenv NODE_VERSIONOutput yang Dihasilkan:
ditimpaTimpaan hanya berlaku untuk container itu — docker run myapp:1.0 berikutnya tanpa -e kembali melihat nilai bawaan image. Tabel kecil berikut merangkum hirarki sumber environment di dalam container:
| Sumber | Kapan diset | Bisa ditimpa runtime? |
|---|---|---|
| ENV pada Dockerfile | saat build; menempel permanen di image | ya — -e menang untuk container itu |
-e dan --env-file | saat docker run | sumber tertinggi sebelum container start |
| HOSTNAME | otomatis oleh daemon saat create | tidak — nilainya ditetapkan Docker |
Aplikasi yang Membaca Environment #
Docker hanya menyediakan variabel; aplikasilah yang memutuskan apa yang dibaca. Node.js mengekspos seluruh environment melalui process.env, dan kita bisa membuktikannya tanpa memodifikasi server.js sama sekali:
$ docker run --rm -e NODE_ENV=production myapp:1.0 node -e "console.log('NODE_ENV:', process.env.NODE_ENV)"Output yang Dihasilkan:
NODE_ENV: productionMari kita telaah posisi masing-masing pihak:
node -emenjalankan potongan kode satu baris — pola command override dari Chapter Command Override yang sekaligus membuktikan variabel terbaca oleh proses Node di dalam container.NODE_ENVadalah konvensi ekosistem Node — framework seperti Express membacanya untuk memilih mode production atau development; server.js milik myapp belum membacanya, sehingga perilaku aplikasi memang belum berubah — variabel tersedia, tinggal kode yang memutuskan memakainya.- Berbeda dengan image resmi — image seperti postgres justru membaca env-nya dari entrypoint-nya sendiri; mengubah variabel benar-benar mengubah apa yang dikerjakan container saat menyala.
Pola Variabel Image Resmi #
Setiap image resmi mendefinisikan nama variabelnya sendiri — konsisten di dalam satu image, tetapi tidak universal antar image. Tabel berikut merangkum nama yang paling sering kita jumpai:
| Image | Variabel yang sering dipakai | Efeknya |
|---|---|---|
| postgres | POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_USER | dibaca entrypoint saat inisialisasi pertama basis data |
| mysql | MYSQL_ROOT_PASSWORD, MYSQL_DATABASE | kata sandi root dan basis data awal saat inisialisasi |
| wordpress | WORDPRESS_DB_HOST, WORDPRESS_DB_USER, WORDPRESS_DB_PASSWORD, WORDPRESS_DB_NAME | dipetakan menjadi konstanta wp-config |
| aplikasi Node | NODE_ENV | konvensi ekosistem Node; dibaca aplikasi, bukan image |
| redis | tanpa variabel konfigurasi khusus | dikonfigurasi lewat argumen seperti redis-server --requirepass — kembali ke pola command override |
Demo: postgres dengan Dua Variabel #
Kita tutup chapter dengan demo lengkap memakai image postgres yang sudah berulang kali muncul dalam course ini. Dua variabel dibaca entrypoint-nya saat direktori data masih kosong:
$ docker run -d --name db-katalog \
-e POSTGRES_PASSWORD=rahasia123 \
-e POSTGRES_DB=katalog \
postgres:18-alpineOutput yang Dihasilkan:
c4d8e1f3a7b2950d6e8c1a4b7d3f5e2c9a6b8d1f3e5c7a9b2d4f6e8c0a3b5d7fBila image postgres:18-alpine belum ada di lokal, daemon menariknya otomatis — kebiasaan yang melekat sejak Chapter Menjalankan Container Pertama. Setelah beberapa detik inisialisasi, kedua variabel bisa diverifikasi dari dalam:
$ docker exec db-katalog printenv POSTGRES_DBOutput yang Dihasilkan:
katalog$ docker exec db-katalog pg_isreadyOutput yang Dihasilkan:
/var/run/postgresql:5432 - accepting connectionsMari kita telaah rangkaian demo penutup ini:
POSTGRES_DB=katalogbenar-benar dipakai — entrypoint postgres membaca variabel itu saat menyiapkan direktori data yang masih kosong, lalu membuat basis data bernamakatalog; variabel bukan sekadar catatan, melainkan input kerja entrypoint.POSTGRES_PASSWORD=rahasia123— kata sandi superuser untuk inisialisasi pertama; tanpa variabel ini container postgres menolak jalan, persis peringatan yang kita saksikan pada Chapter Use Case Docker.pg_isready— alat bawaan image yang melaporkan server sudah menerima koneksi;accepting connectionsmenandakan inisialisasi selesai dan variabel-variabel tadi sudah diproses habis.
Environment variable mengatur bagaimana aplikasi berperilaku, tetapi masih menyisakan satu pertanyaan identitas: sebagai user siapa seluruh proses itu bekerja? Jawaban bawaan Docker mengejutkan banyak orang — hampir semuanya berjalan sebagai root, user nomor 0 dengan hak administratif penuh. Chapter User dan Permissions membedah identitas proses container, flag --user untuk menimpanya, dan konsekuensi izin berkas yang mengikuti angka UID itu.
Referensi #
- docker container run — CLI Reference
— flag
-e, bentuk pass-through, dan--env-file. - docker container exec — CLI Reference — verifikasi environment dari dalam container yang sedang berjalan.
- PostgreSQL — Docker Hub — daftar variabel seperti POSTGRES_PASSWORD dan POSTGRES_DB yang dibaca entrypoint-nya.