SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-07

Perbezaan .env, env_file dan environment Docker Compose

Ketahui perbezaan antara .env, env_file dan environment dalam Docker Compose. Kami terangkan susunan keutamaan dan cara mengurus rahsia sensitif dengan betul dalam container.

Tiga perkara yang dipanggil sebagai fail env

Docker Compose mempunyai tiga mekanisme berasingan dengan nama yang mengelirukan. Fail .env mengisi pemegang tempat ${VARIABLE} di dalam compose.yaml itu sendiri, sebelum Compose memproses fail tersebut. Atribut env_file: memuatkan fail pasangan kunci/nilai ke dalam persekitaran container. Atribut environment: menetapkan pemboleh ubah secara terus pada container, yang ditulis di dalam fail compose. Ia tidak boleh ditukar ganti, dan apabila dua daripadanya menetapkan kunci yang sama, pemenang ditentukan oleh susunan keutamaan yang didokumentasikan.

Panduan ini menunjukkan cara setiap satunya berfungsi, membuktikan keutamaan dengan arahan yang boleh anda jalankan, kemudian membincangkan bahagian yang lebih penting: pemboleh ubah persekitaran boleh dibaca oleh sesiapa sahaja yang boleh menjalankan docker inspect, jadi kata laluan tidak sepatutnya diletakkan di dalamnya. Jika anda baru menggunakan fail compose secara umum, mulakan dengan Asas Docker Compose pada VPS dan kembali ke sini untuk konfigurasi.

Fail .env adalah untuk fail compose, bukan untuk container

Cipta satu direktori dan letakkan dua fail di dalamnya.

mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .env
services:
  demo:
    image: alpine:${ALPINE_TAG}
    command: printenv ALPINE_TAG

Sekarang, tanya Compose perkara yang sebenarnya telah dihurai (parsed).

docker compose config

Output menunjukkan image: alpine:3.20. Pemegang tempat (placeholder) telah hilang kerana interpolasi berlaku semasa waktu penghuraian. Compose mencari .env dalam direktori projek, iaitu direktori yang menyimpan fail compose, dan menggantikan setiap ${NAME} yang ditemuinya.

Kemudian, jalankan servis tersebut.

docker compose run --rm demo

printenv ALPINE_TAG keluar dengan status 1 dan tidak mencetak apa-apa. Pemboleh ubah tersebut tidak wujud di dalam container. Ini adalah salah faham yang paling lazim: .env mengkonfigurasi fail compose, bukan proses tersebut. Fail .env yang mengandungi POSTGRES_PASSWORD=hunter2 tidak memberikan sebarang kesan kepada pangkalan data anda melainkan terdapat bahagian dalam fail compose yang merujuk kepadanya.

${NAME:-default} menyediakan nilai sandaran (fallback) apabila pemboleh ubah tidak ditetapkan atau kosong. ${NAME:?message} menyebabkan Compose enggan bermula dan mencetak mesej anda, yang merupakan pilihan tepat bagi nilai yang tidak mempunyai nilai lalai yang selamat.

env_file memuatkan pemboleh ubah ke dalam kontena

Atribut env_file: menamakan satu atau lebih fail yang kandungannya menjadi pemboleh ubah persekitaran kontena.

printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.env
services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
docker compose run --rm demo

Ini akan mencetak from_env_file. Format fail adalah baris KEY=value biasa, satu untuk setiap baris, dengan # memulakan ulasan. Ia bukan shell. Tanda petikan dikekalkan sebagai sebahagian daripada nilai dalam kebanyakan kes, dan awalan export tidak diperlukan. Jangan letakkan ruang di sekeliling tanda =, kerana KEY = value akan menghasilkan pemboleh ubah yang dinamakan secara literal sebagai KEY dengan ruang di hadapan nilainya.

Laluan env_file yang tiada akan menyebabkan ralat dan Compose akan berhenti. Tandakan ia sebagai pilihan jika fail tersebut mungkin tiada secara sah:

    env_file:
      - path: ./app.env
        required: false

persekitaran menetapkan pemboleh ubah secara inline

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    environment:
      GREETING: from_environment

Dua sintaks diterima, iaitu bentuk pemetaan di atas dan bentuk senarai menggunakan - GREETING=from_environment. Kedua-duanya berfungsi secara identik. Bentuk senarai mempunyai satu helah tambahan: kunci kosong tanpa nilai akan melalukan pemboleh ubah daripada shell tempat anda menjalankan docker compose.

    environment:
      - GREETING
GREETING=from_my_shell docker compose run --rm demo

Arahan itu mencetak from_my_shell. Jalankannya tanpa menetapkan GREETING dalam shell dan Compose tidak menetapkan apa-apa, tanpa sebarang amaran. Kegagalan laluan senyap ini perlu diketahui, kerana servis yang bermula dengan pemboleh ubah kata laluan kosong sering kali bermula dengan jayanya dan dibiarkan terbuka luas.

Which one wins

Docker documents the precedence order, highest first: docker compose run -e on the command line, then environment or env_file whose value is interpolated from your shell or from an env file, then plain environment in the compose file, then env_file, then the ENV directive baked into the image.

The short version for daily work: environment: beats env_file:, and -e on the command line beats both. Prove it in one file.

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
    environment:
      GREETING: from_environment
docker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETING

The first prints from_environment, so environment: overrode the value in app.env. The second prints from_cli. Nothing in the compose file overrides the command line.

When a container is behaving as though your config never applied, do not guess. docker compose config prints the fully resolved file, and docker compose config --environment prints the interpolation variables Compose is working from. Most "my env file is ignored" reports turn out to be a value set twice at two different levels.

Mengapa pemboleh ubah persekitaran bocor

Tetapkan kata laluan dalam environment: dan ia akan disimpan dalam konfigurasi kontena pada cakera, yang boleh dilihat oleh mana-mana pengguna dalam kumpulan docker.

docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'

Output tersebut mengandungi "DB_PASSWORD=hunter2" dalam teks biasa. Tiga lagi laluan mendedahkan nilai yang sama. docker compose config mencetaknya ke terminal, yang merupakan punca ia akhirnya ditampal ke dalam forum sokongan. Mana-mana proses di dalam kontena boleh membaca /proc/1/environ dan setiap proses anak mewarisi pemboleh ubah tersebut. Selain itu, pengendali ranap aplikasi secara rutin membuang keseluruhan persekitaran ke dalam log atau laporan ralat.

Keahlian kumpulan docker secara efektif adalah root pada hos, jadi ini bukanlah sempadan keistimewaan yang boleh anda harapkan. Panduan mengenai akaun pengguna keistimewaan minimum pada VPS menjelaskan sebab kumpulan tersebut perlu dihadkan pada mana-mana kotak kongsi.

Compose secrets menyimpan nilai dalam fail

Compose menyokong rahsia berasaskan fail. Nilai tersebut dimount ke dalam kontena sebagai fail dan bukannya disuntik ke dalam persekitaran.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt

Rahsia tersebut dimount pada /run/secrets/db_password di dalam kontena. Nama selepas garis miring ialah nama rahsia daripada blok peringkat atas secrets:.

Akhiran _FILE ialah konvensyen yang digunakan oleh Imej Rasmi Docker, termasuk postgres, mysql dan mariadb. Skrip entrypoint tersebut menyemak VARNAME_FILE, membaca fail itu, dan menggunakan kandungannya. Ini bukan ciri Docker, jadi ia hanya berfungsi jika imej tersebut melaksanakannya. Semak dokumentasi imej sebelum menganggap SOMETHING_FILE akan dipatuhi. Aplikasi yang tidak menyokongnya sering kali boleh membaca fail itu sendiri semasa permulaan, atau anda boleh menghantar laluan tersebut dan membiarkan entrypoint anda sendiri yang melakukannya.

Sahkan dari dalam kontena yang sedang berjalan:

docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORD

Perintah pertama mencetak kata laluan. Perintah kedua tidak mencetak apa-apa, kerana nilai tersebut tidak pernah dimasukkan ke dalam persekitaran. Itulah tujuan utamanya: docker inspect pada kontena ini hanya menunjukkan laluan yang tidak berbahaya.

Lindungi fail sumber pada hos, kerana kerahsiaan rahsia tersebut bergantung pada fail di sebaliknya:

chmod 600 db_password.txt

Jalan tengah yang pragmatik pada VPS

Banyak imej yang dihoskan sendiri tidak menyokong pemboleh ubah _FILE, jadi pemboleh ubah persekitaran adalah satu-satunya cara untuk memasukkannya. Pada VPS dengan pentadbir tunggal, matlamat yang realistik adalah untuk menghalang nilai tersebut daripada berada dalam fail yang boleh dibaca oleh semua orang dalam direktori projek anda, dan untuk menyimpannya di luar git.

sudo install -o root -g root -m 600 /dev/null /etc/myapp/app.env
sudo nano /etc/myapp/app.env
    env_file:
      - /etc/myapp/app.env

install -m 600 mencipta fail dengan mod yang telah ditetapkan, jadi tiada tempoh di mana fail tersebut boleh dibaca oleh sesiapa sahaja. Root memilikinya, jadi pengguna bukan root pada mesin tersebut tidak boleh membacanya, walaupun sesiapa yang boleh menjalankan docker masih boleh membaca nilai tersebut daripada kontena. Tambahkan *.env dan .env ke dalam .gitignore dan lakukan commit pada app.env.example yang mengandungi nama kunci dengan nilai kosong sebagai gantinya. Kata laluan yang telah di-commit adalah kata laluan yang perlu ditukar.

Menukar nilai bermakna memulakan semula servis. Pemboleh ubah persekitaran dibaca sekali sahaja apabila proses kontena bermula, jadi menyunting fail tidak mengubah apa-apa sehingga anda menjalankan docker compose up -d --force-recreate db. Ini adalah corak yang sama yang digunakan dalam panduan n8n di sebalik HTTPS pada VPS, di mana kunci penyulitan berada di luar fail compose.

Memisahkan konfigurasi mengikut persekitaran

Compose membaca .env daripada direktori projek secara lalai. Halakan ia ke lokasi lain dengan --env-file.

docker compose --env-file .env.staging config

Berbilang fail dibaca mengikut urutan, dan fail yang dibaca kemudian akan mengatasi fail sebelumnya. Simpan tetapan lalai yang bukan rahsia dalam fail yang di-commit, dan simpan rahsia dalam fail yang tidak pernah meninggalkan pelayan. Perkara yang sama terpakai pada env_file:, di mana fail terakhir yang disenaraikan akan diguna pakai bagi kunci yang bertindih.

FAQ

Mengapa fail .env saya diabaikan di dalam container?

Ia tidak diabaikan. Fail .env hanya menggantikan placeholder ${NAME} di dalam fail compose. Ia tidak menetapkan pemboleh ubah di dalam container. Untuk memasukkan nilai tersebut ke dalam container, buat rujukan kepadanya: environment: { KEY: "${NAME}" }, atau gunakan env_file: ./that-file.env sebagai ganti.

Adakah environment mengatasi env_file, atau sebaliknya?

environment: lebih diutamakan. Susunan yang didokumentasikan oleh Docker meletakkan atribut environment di atas atribut env_file, dan kedua-duanya berada di bawah docker compose run -e pada baris perintah. Jika sesuatu kunci ditetapkan di kedua-dua tempat, nilai dalam env_file tidak akan digunakan tanpa sebarang amaran.

Bagaimanakah cara untuk melihat nilai akhir yang akan digunakan oleh Compose?

Jalankan docker compose config untuk mencetak fail compose yang telah diselesaikan sepenuhnya dengan semua interpolasi digunakan. Bagi container yang sedang berjalan, docker inspect <container> --format '{{json .Config.Env}}' menunjukkan dengan tepat apa yang diterima oleh prosesnya.

Adakah secret Compose disulitkan?

Tidak. Secret berasaskan fail dimount ke dalam container sebagai fail biasa pada /run/secrets/<name>, dan fail sumber tersebut berada pada cakera hos tanpa disulitkan. Kelebihannya adalah dari segi skop, bukan penyulitan: nilai tersebut kekal di luar persekitaran container, di luar output docker inspect, dan di luar dump ranap (crash dump) yang mencetak persekitaran.

Bolehkah saya menggunakan tanda petikan dan ruang kosong dalam fail env?

Gunakan KEY=value with spaces dan jangan gunakan tanda petikan. Compose menganggap keseluruhan bahagian baris yang selebihnya sebagai nilai, jadi tanda petikan biasanya akan berakhir sebagai aksara literal dalam nilai tersebut. Jangan sekali-kali meletakkan ruang kosong di sekeliling =, kerana kunci tersebut akan membawa ruang kosong tambahan dan tiada apa yang akan sepadan dengannya.