# Cara Menyimpan Secret Environment Variable Docker Aman

> Cara menyimpan secret environment variable Docker dengan aman: kenapa ENV dan ARG bocor, lalu env_file, secret mount, GitHub Actions, dan rotasi kunci.

URL: https://www.matthewswong.com/id/blog/cara-simpan-secret-environment-variable-docker-aman/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-10-17
- Updated: 2026-10-17
- Category: DevOps

> **Key takeaway:** Cara menyimpan secret environment variable Docker dengan aman: jangan taruh di ENV atau ARG, karena keduanya berakhir di image. Simpan di file .env yang diabaikan Git dan .dockerignore, kirim ke container saat runtime lewat --env-file atau env_file, pakai secret mount untuk kredensial build, dan segera putar ulang kunci yang pernah ter-commit.

Aplikasimu jalan di laptop karena ada file .env berisi API key. Lalu kamu mau menjalankannya di container, dan muncul pertanyaan yang hampir semua pemula alami: kuncinya ditaruh di mana? Jalan pintasnya terlihat jelas, yaitu menulis ENV API_KEY=... di Dockerfile. Itu jalan yang membuat kunci ikut terkirim ke registry bersama image, terbaca oleh siapa pun yang bisa menarik image itu.

Panduan ini membandingkan empat tempat untuk sebuah nilai: ARG, ENV, environment variable saat runtime, dan secret mount, lalu membawanya sampai GitHub Actions dan apa yang harus dilakukan kalau kunci terlanjur masuk Git. Semua perintah dicocokkan dengan dokumentasi resmi Docker dan GitHub, dan pola deploy-nya sama dengan yang dipakai situs ini: secret disimpan di GitHub, ditulis ke file .env milik pemilik saja di server, lalu dibaca container saat start. Semua nilai di lab adalah karangan.

Mau memahami konsepnya dulu, lengkap dengan animasi alurnya? Baca pelajaran gratis kami:

Belajar environment variable dan secret Docker

. Artikel ini sisi prakteknya: kamu mengetik perintah dan melihat sendiri di mana nilai bocor dan di mana aman.

## Yang kamu butuhkan

Tidak banyak. Semua langkah berjalan di Linux, macOS, maupun Windows selama Docker aktif.

- Docker terpasang dan berjalan (Docker Desktop di macOS atau Windows, Docker Engine di Linux).
- Terminal dan satu editor teks. Docker Compose dipakai di Langkah 4; versi Docker modern sudah menyertakannya.
- Sekitar 30 menit dan folder kosong bernama secret-lab.

Pakai nilai karangan seperti not-a-real-secret. Jangan pernah menempelkan kunci asli ke lab latihan, apalagi ke perintah yang masuk riwayat terminal.

## Langkah 1: Lihat sendiri kenapa ARG dan ENV bukan tempat secret

Mulai dari ARG. Dokumentasi Dockerfile menyatakan nilai ARG tidak tertanam di image dan tidak ada di container akhir, tetapi build argument tetap terlihat di docker history, dan Docker tidak menyarankan memakainya untuk secret. Buktikan sendiri:

Sekarang ENV. Dokumentasi menyebut nilai ENV selalu disimpan di image hasil build dan terbawa ke setiap container yang dijalankan darinya. Tidak perlu docker history; cukup jalankan containernya:

> Kalau sebuah kunci pernah masuk ARG atau ENV lalu image-nya di-push, anggap kunci itu bocor. Putar ulang (rotate) di penyedianya; menghapus image saja tidak menarik kembali salinan yang sudah ditarik orang lain.

Ringkasnya, ENV dan ARG cocok untuk nilai yang boleh dibaca siapa pun, misalnya NODE_ENV atau nomor port. Dockerfile situs ini hanya berisi ENV seperti itu dan tidak mendeklarasikan satu pun ARG.

## Langkah 2: Jauhkan file .env dari Git dan dari image

Dua file mencegah dua kebocoran berbeda. .gitignore menjaga .env dari repository. .dockerignore menjaga .env dari build context, dan Docker menyatakan file yang cocok dengan polanya dihapus dari build context sebelum dikirim ke builder, sehingga tidak bisa disalin ke image dengan ADD, COPY, atau bind mount. Repo situs ini mencantumkan .env dan .env.* di kedua file.

Perhatikan bahwa .gitignore hanya berlaku untuk file yang belum pernah di-commit. Kalau .env sudah terlanjur masuk Git, menambahkannya ke .gitignore sekarang tidak menghapus riwayatnya; itu urusan Langkah 7.

## Langkah 3: Kirim secret saat runtime dengan --env-file atau -e

Pola yang benar: image dibangun tanpa secret, lalu nilainya diberikan ke container ketika ia dijalankan. Dengan --env-file, setiap baris berbentuk VAR=value, dan baris yang diawali tanda # adalah komentar. Dengan -e VAR tanpa tanda sama dengan, Docker CLI mengambil nilai VAR dari environment shell-mu.

Satu jebakan kecil: dokumentasi menyatakan tanda # di tengah baris adalah bagian dari nilai, bukan komentar. Jadi komentar di akhir baris ikut masuk ke dalam secret-mu dan membuat autentikasi gagal tanpa pesan jelas.

> Pakai -e VAR tanpa nilai ketika kamu tidak mau mengetik kunci di baris perintah. Kalau variabelnya tidak di-export di shell, dokumentasi menyebut variabel itu tidak diatur di container.

## Langkah 4: Pindahkan ke Docker Compose dengan env_file

Di server, kamu hampir pasti memakai Compose. Atribut env_file memuat file .env ke environment container, dengan path relatif terhadap lokasi compose.yaml. Setelah file dibuat, batasi izinnya dengan chmod 600 supaya hanya pemilik yang bisa membacanya, seperti yang dilakukan deploy situs ini.

Kalau nama yang sama diatur di dua tempat, Compose punya urutan tetap. Dari yang tertinggi: docker compose run -e, lalu environment atau env_file dengan nilai hasil interpolasi, lalu environment saja, lalu env_file saja, dan terakhir ENV di image. Karena itu entri environment menang atas env_file, seperti pada contoh di atas.

Satu catatan jujur: dokumentasi Compose menyarankan agar password tidak diteruskan lewat environment variable dan menawarkan Compose secrets. env_file tetap masuk akal untuk satu container di satu server dengan file milik pemilik saja, seperti situs ini. Untuk setup yang lebih besar, pelajari opsi secrets itu.

## Langkah 5: Pakai secret mount kalau build-nya yang butuh kredensial

Kadang build perlu kredensial, misalnya untuk mengunduh package privat. Docker menulis bahwa build argument dan environment variable tidak pantas untuk ini karena bertahan di image akhir, dan menyarankan secret mount. Nilainya muncul sebagai file di /run/secrets/id-nya hanya selama satu instruksi RUN itu.

Situs ini tidak memerlukannya, jadi bagian ini murni dari dokumentasi Docker. Untuk secret yang dibutuhkan aplikasi saat berjalan, tetap pakai Langkah 3 dan 4.

## Langkah 6: Bawa secret dari GitHub Actions ke server

Simpan nilainya di GitHub sebagai secret dan baca lewat konteks secrets. Dokumentasi GitHub menyarankan meneruskan secret ke perintah sebagai environment variable yang dirujuk dengan tanda kutip, dan memperingatkan agar menghindari secret di baris perintah karena proses lain bisa melihatnya lewat ps.

Deploy situs ini menulis file .env di server lewat SSH dengan heredoc bertanda kutip, lalu chmod 600. Bentuknya begini, dengan nama yang diganti placeholder:

Jangan mengandalkan penyamaran log. GitHub memang menyamarkan secret yang tercetak, tetapi dokumentasinya menyatakan penyamaran itu tidak dijamin karena nilai bisa diubah bentuknya, dan runner hanya bisa menyamarkan secret yang dipakai di job saat ini. Jangan pernah mencetak secret dengan echo.

## Langkah 7: Kalau secret terlanjur ter-commit atau ter-push

Urutannya penting. Dokumentasi GitHub menyebut langkah pertama adalah mencabut atau memutar ulang secret itu; setelah diputar, kunci lama tidak bisa dipakai lagi, dan menulis ulang riwayat mungkin tidak perlu. Kalau kamu tetap ingin membersihkan riwayat, GitHub menyarankan git-filter-repo versi 2.47 atau lebih baru dengan opsi --sensitive-data-removal:

> Menulis ulang riwayat dan force push tidak cukup. GitHub memperingatkan commit lama masih bisa diakses di clone dan fork lain, lewat tampilan cache berdasarkan hash SHA-1, dan lewat pull request yang merujuknya. Itulah alasan putar ulang kunci selalu langkah pertama.

Setelah itu, kembali ke Langkah 2 supaya kejadiannya tidak berulang. Pelajari juga cara kerja secret scanning di artikel pendukung di bagian akhir.

## Error umum dan cara memperbaikinya

Enam gejala yang paling sering membuat pemula bingung, beserta penyebab dan perbaikannya.

| Gejala | Penyebab | Perbaikan |
| --- | --- | --- |
| Kunci terlihat di output docker history | Nilai dikirim lewat ARG atau build-arg | Putar ulang kunci, lalu pindah ke RUN --mount=type=secret |
| printenv di container kosong padahal ada di .env | Lupa --env-file atau env_file, atau memakai -e VAR yang tidak di-export di shell | Tambahkan --env-file .env atau env_file: .env, atau export variabelnya dulu |
| Autentikasi gagal padahal kunci benar | Komentar di akhir baris .env ikut menjadi bagian nilai | Pindahkan komentar ke baris sendiri yang diawali tanda # |
| Nilai dari environment dipakai, bukan dari .env | Dalam prioritas Compose, environment berada di atas env_file | Hapus entri yang ganda, atau sengaja pakai environment sebagai override |
| Compose jalan dengan nilai kosong tanpa peringatan | Variabel belum diatur, dan interpolasi biasa memakai string kosong | Pakai bentuk wajib ada dengan tanda tanya dan pesan error, supaya Compose berhenti dengan error |
| Secret tetap muncul di log GitHub Actions | Penyamaran tidak dijamin untuk nilai yang diubah bentuknya, dan hanya berlaku untuk secret di job itu | Jangan mencetak secret; putar ulang kunci yang sempat tercetak |

Semua gejala di atas bisa kamu reproduksi di lab ini dengan nilai karangan, jadi coba sengaja salah dulu supaya mengenali wajahnya.

## Checklist sebelum deploy

.env dan .env.* ada di .gitignore dan .dockerignore.

Dockerfile hanya berisi ENV yang tidak sensitif, dan tidak ada ARG untuk secret.

Secret aplikasi diberikan saat runtime lewat env_file atau --env-file.

File .env di server dibatasi dengan chmod 600.

Secret di GitHub Actions dirujuk lewat env dengan tanda kutip, tidak ditulis di baris perintah dan tidak dicetak.

Kamu tahu urutan kalau kunci bocor: putar ulang dulu, baru bersihkan riwayat.

## Belajar lebih dalam

Untuk gambaran besar tempat secret berada di setiap tahap deploy, ikuti pelajaran

, lengkap dengan kuis dan animasi. Untuk kebiasaan Dockerfile yang baik secara umum, baca

. Dan kalau kamu ingin melangkah ke Docker secrets di production, lanjut ke

.

Aturan yang bisa kamu bawa pulang: image dibangun tanpa secret, secret datang saat runtime, dan kunci yang pernah terlihat orang lain dianggap bocor dan diputar ulang. Sisanya hanyalah memilih tempat yang tepat untuk setiap nilai.

## Sumber dan referensi:

- Dokumentasi Docker: referensi Dockerfile (ARG dan ENV)
- Dokumentasi Docker: build secrets
- Dokumentasi Docker: docker container run (-e dan --env-file)
- Dokumentasi Docker: prioritas environment variable di Compose
- Dokumentasi GitHub: menggunakan secrets di GitHub Actions
- Dokumentasi GitHub: menghapus data sensitif dari repository

## Source links

- https://docs.docker.com/reference/dockerfile/
- https://docs.docker.com/build/building/secrets/
- https://docs.docker.com/reference/cli/docker/container/run/
- https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/
- https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets
- https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository

## FAQ

### Apakah aman menaruh API key di ENV Dockerfile?

Tidak aman. Dokumentasi Docker menyebut nilai ENV selalu disimpan di image hasil build dan terbawa ke setiap container, jadi siapa pun yang bisa menarik image bisa membaca kuncinya. Kirim lewat --env-file atau env_file saat runtime, dan putar ulang kunci yang sudah terlanjur tertanam.

### Kenapa build argument saya masih muncul di docker history?

ARG tidak tertanam di image akhir, tetapi Docker menyatakan build argument terlihat di docker history. Itu sebabnya Docker tidak menyarankannya untuk secret. Pakai RUN --mount=type=secret ketika build butuh kredensial.

### Apakah .dockerignore mencegah file .env masuk ke image?

Ya. File yang cocok dengan pola .dockerignore dihapus dari build context sebelum dikirim ke builder, dan dokumentasi menyebut file itu tidak bisa disalin ke image dengan ADD, COPY, atau bind mount. Tetap kirim nilainya saat runtime, karena image tidak boleh bergantung pada file itu.

### Apa yang harus dilakukan kalau secret terlanjur ter-commit ke GitHub?

Cabut atau putar ulang secret itu lebih dulu. Langkah itu saja mungkin cukup karena kunci lama berhenti berfungsi. Kalau kamu juga menulis ulang riwayat dengan git-filter-repo dan force push, ingat GitHub memperingatkan commit itu masih bisa diakses lewat fork, clone, tampilan cache, dan pull request.

### Mana yang menang di Docker Compose, environment atau env_file?

Atribut environment menang atas env_file ketika keduanya tidak memakai nilai hasil interpolasi. Nilai dari docker compose run -e mengalahkan keduanya, dan ENV yang tertanam di image punya prioritas paling rendah.

---

- HTML version: https://www.matthewswong.com/id/blog/cara-simpan-secret-environment-variable-docker-aman/
- English: https://www.matthewswong.com/en/blog/cara-simpan-secret-environment-variable-docker-aman/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
