SSD Nodes Learn RAM 8GB — $66/ปี
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-01

Docker Compose: .env, env_file และ environment

ทำความเข้าใจ .env, env_file และ environment ใน Docker Compose พร้อมลำดับความสำคัญที่ชนะกัน และเหตุผลที่ secrets ไม่ควรเก็บไว้ในตัวแปรสภาพแวดล้อม

3 สิ่งที่เรียกว่าไฟล์ env

Docker Compose มีกลไก 3 แบบที่มีชื่อคล้ายกันจนทำให้สับสน ไฟล์ .env ใช้แทนค่าตัวแปร ${VARIABLE} ภายในไฟล์ compose.yaml เอง ก่อนที่ Compose จะวิเคราะห์ไฟล์ด้วยซ้ำ แอตทริบิวต์ env_file: โหลดคู่คีย์/ค่าลงใน environment ของคอนเทนเนอร์ แอตทริบิวต์ environment: กำหนดตัวแปรให้คอนเทนเนอร์โดยตรง โดยเขียนไว้ในไฟล์ compose กลไกเหล่านี้ใช้แทนกันไม่ได้ และเมื่อมี 2 กลไกกำหนดคีย์เดียวกัน ผลลัพธ์จะเป็นไปตามลำดับความสำคัญที่มีการระบุไว้ในเอกสาร

คู่มือนี้จะแสดงการทำงานของแต่ละกลไก พิสูจน์ลำดับความสำคัญด้วยคำสั่งที่คุณเรียกใช้ได้ จากนั้นจะกล่าวถึงประเด็นที่สำคัญกว่า: ทุกคนที่เรียกใช้ docker inspect ได้สามารถอ่าน environment variables ได้ ดังนั้นไม่ควรเก็บรหัสผ่านไว้ในตัวแปรเหล่านี้ หากคุณยังไม่คุ้นเคยกับไฟล์ compose โดยทั่วไป ให้เริ่มจาก พื้นฐาน Docker Compose บน VPS แล้วกลับมาที่หน้านี้เพื่อศึกษาการกำหนดค่า

ไฟล์ .env ใช้สำหรับไฟล์ compose ไม่ใช่คอนเทนเนอร์

สร้างไดเรกทอรีและวางไฟล์ 2 ไฟล์ไว้ในนั้น

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

จากนั้นให้ Compose แสดงสิ่งที่แยกวิเคราะห์จริง

docker compose config

ผลลัพธ์แสดง image: alpine:3.20 ตัวแทนถูกแทนที่แล้ว เนื่องจากการแทนค่าตัวแปรเกิดขึ้นขณะแยกวิเคราะห์ Compose จะค้นหา .env ในไดเรกทอรีโครงการ ซึ่งเป็นไดเรกทอรีที่เก็บไฟล์ compose และแทนที่ ${NAME} ทุกตำแหน่งที่พบ

จากนั้นให้เริ่ม service

docker compose run --rm demo

printenv ALPINE_TAG จบการทำงานด้วยสถานะ 1 และไม่แสดงผลลัพธ์ ตัวแปรนี้ไม่มีอยู่ภายในคอนเทนเนอร์ นี่คือความเข้าใจผิดที่พบบ่อยที่สุด: .env ใช้กำหนดค่าให้ไฟล์ compose ไม่ใช่ให้กระบวนการ ไฟล์ .env ที่มี POSTGRES_PASSWORD=hunter2 อยู่ภายในจะไม่ส่งผลใด ๆ ต่อฐานข้อมูล เว้นแต่ส่วนใดส่วนหนึ่งของไฟล์ compose จะอ้างอิงไฟล์ดังกล่าว

${NAME:-default} กำหนดค่าเริ่มต้นสำรองเมื่อไม่ได้ตั้งค่าตัวแปรหรือมีค่าเป็นค่าว่าง ${NAME:?message} ทำให้ Compose ปฏิเสธการเริ่มทำงานและแสดงข้อความของคุณ ซึ่งเป็นตัวเลือกที่เหมาะสมสำหรับค่าที่ไม่มีค่าเริ่มต้นที่ปลอดภัย

env_file โหลดตัวแปรเข้าสู่คอนเทนเนอร์

แอตทริบิวต์ env_file: ระบุไฟล์อย่างน้อย 1 ไฟล์ ซึ่งเนื้อหาจะถูกใช้เป็นตัวแปรสภาพแวดล้อมของคอนเทนเนอร์

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

คำสั่งนี้จะแสดงผล from_env_file รูปแบบไฟล์เป็นบรรทัด KEY=value ธรรมดา โดยใช้ 1 ตัวแปรต่อ 1 บรรทัด และ # ใช้เริ่มต้นความคิดเห็น ไฟล์นี้ไม่ใช่สคริปต์ shell โดยทั่วไป เครื่องหมายอัญประกาศจะยังเป็นส่วนหนึ่งของค่า และไม่จำเป็นต้องใช้คำนำหน้า export ห้ามเว้นวรรคก่อนหรือหลังเครื่องหมาย = เนื่องจาก KEY = value จะสร้างตัวแปรที่มีชื่อเป็น KEY และมีช่องว่างนำหน้าอยู่ในค่า

หากไม่พบพาธ env_file จะถือเป็นข้อผิดพลาด และ Compose จะหยุดทำงาน ให้ระบุไฟล์เป็นรายการทางเลือก หากไฟล์อาจไม่มีอยู่โดยชอบด้วยเหตุผล:

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

environment sets variables inline

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

รองรับไวยากรณ์ 2 รูปแบบ ได้แก่ รูปแบบการแมปด้านบนและรูปแบบรายการที่ใช้ - GREETING=from_environment ทั้งสองรูปแบบมีพฤติกรรมเหมือนกัน รูปแบบรายการมีคุณสมบัติเพิ่มเติมอย่างหนึ่ง คือคีย์เปล่าที่ไม่มีค่าจะส่งต่อตัวแปรจาก shell ที่คุณเรียกใช้ docker compose

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

คำสั่งนี้จะแสดง from_my_shell หากเรียกใช้โดยไม่ได้กำหนด GREETING ใน shell, Compose จะไม่กำหนดค่าใด ๆ และจะไม่แสดงคำเตือน ควรทราบว่าการส่งต่อตัวแปรที่ล้มเหลวโดยไม่มีข้อความแจ้งเตือนอาจทำให้เกิดปัญหาได้ เนื่องจาก service ที่เริ่มทำงานพร้อมตัวแปรรหัสผ่านว่างมักเริ่มทำงานได้สำเร็จ แต่ไม่มีการป้องกันใด ๆ

รายการใดมีผลเหนือกว่า

Docker ระบุลำดับความสำคัญจากสูงไปต่ำดังนี้: docker compose run -e บนบรรทัดคำสั่ง ตามด้วย environment หรือ env_file ซึ่งค่าถูกแทนที่จาก shell หรือจากไฟล์ env จากนั้นคือ environment แบบปกติในไฟล์ compose ตามด้วย env_file และสุดท้ายคือคำสั่ง ENV ที่ถูกฝังไว้ใน image

สรุปสั้นๆ สำหรับการใช้งานประจำวัน: environment: มีผลเหนือกว่า env_file: และ -e บนบรรทัดคำสั่งมีผลเหนือกว่าทั้งสองค่า ทดสอบได้ในไฟล์เดียว

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

คำสั่งแรกแสดง from_environment ซึ่งหมายความว่า environment: แทนที่ค่าใน app.env คำสั่งที่สองแสดง from_cli ไม่มีค่าใดในไฟล์ compose ที่แทนที่ค่าจากบรรทัดคำสั่งได้

เมื่อ container ทำงานราวกับว่าไม่ได้ใช้การกำหนดค่าของคุณ อย่าคาดเดา docker compose config จะแสดงไฟล์ที่แก้ค่าครบถ้วนแล้ว และ docker compose config --environment จะแสดงตัวแปร interpolation ที่ Compose ใช้งาน รายงานลักษณะ “ไฟล์ env ของฉันถูกละเว้น” ส่วนใหญ่เกิดจากการกำหนดค่าเดียวกันซ้ำใน 2 ระดับที่แตกต่างกัน

เหตุใดตัวแปรสภาพแวดล้อมจึงรั่วไหล

กำหนดรหัสผ่านใน environment: แล้วรหัสผ่านจะถูกจัดเก็บไว้ในไฟล์การกำหนดค่าของคอนเทนเนอร์บนดิสก์ และผู้ใช้ทุกคนในกลุ่ม docker จะมองเห็นได้

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

ผลลัพธ์มี "DB_PASSWORD=hunter2" อยู่ในรูปแบบข้อความธรรมดา เส้นทางอีก 3 รายการก็เปิดเผยค่าเดียวกัน docker compose config แสดงค่านี้ในเทอร์มินัล จึงอาจถูกคัดลอกไปโพสต์ในฟอรัมขอรับการสนับสนุนได้ กระบวนการใด ๆ ภายในคอนเทนเนอร์สามารถอ่าน /proc/1/environ ได้ และกระบวนการลูกทุกกระบวนการจะสืบทอดตัวแปรนี้ นอกจากนี้ ตัวจัดการข้อขัดข้องของแอปพลิเคชันมักจะดัมพ์สภาพแวดล้อมทั้งหมดลงใน log หรือรายงานข้อผิดพลาด

การเป็นสมาชิกกลุ่ม docker มีสิทธิ์เทียบเท่ากับ root บน host ดังนั้นจึงไม่ใช่ขอบเขตสิทธิ์ที่ควรพึ่งพา คู่มือ บัญชีผู้ใช้ที่มีสิทธิ์น้อยที่สุดบน VPS อธิบายว่าเหตุใดจึงควรจำกัดกลุ่มนี้บนระบบที่มีผู้ใช้ร่วมกันทุกระบบ

Compose secrets เก็บค่าไว้ในไฟล์

Compose รองรับ secrets แบบใช้ไฟล์ ค่า secret จะถูก mount เข้าไปใน container เป็นไฟล์ แทนการ inject เข้าไปใน environment

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

secret จะถูก mount ไว้ที่ /run/secrets/db_password ภายใน container ชื่อหลังเครื่องหมาย slash คือชื่อ secret จากบล็อก secrets: ระดับบนสุด

ส่วนต่อท้าย _FILE เป็น convention ที่ Docker Official Images ใช้ รวมถึง postgres, mysql และ mariadb สคริปต์ entrypoint เหล่านั้นจะตรวจสอบ VARNAME_FILE อ่านไฟล์ และใช้เนื้อหาของไฟล์ ฟังก์ชันนี้ไม่ใช่ฟีเจอร์ของ Docker ดังนั้นจะใช้ได้เฉพาะเมื่อ image นั้นรองรับ ตรวจสอบเอกสารของ image ก่อนสมมติว่า SOMETHING_FILE จะได้รับการรองรับ แอปพลิเคชันที่ไม่รองรับฟังก์ชันนี้มักอ่านไฟล์เองเมื่อเริ่มทำงานได้ หรือคุณสามารถส่ง path ให้แอปพลิเคชัน แล้วให้ entrypoint ของคุณจัดการเอง

ตรวจสอบจากภายใน container ที่กำลังทำงาน:

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

คำสั่งแรกจะแสดง password คำสั่งที่สองไม่แสดงผลใดๆ เพราะค่าไม่เคยถูกส่งเข้าไปใน environment นี่คือจุดประสงค์ทั้งหมด: docker inspect บน container นี้จะแสดงเฉพาะ path ที่ไม่มีความลับ

ป้องกันไฟล์ต้นทางบน host เนื่องจาก secret จะเป็นส่วนตัวได้เท่ากับไฟล์ที่เก็บค่าไว้เท่านั้น:

chmod 600 db_password.txt

แนวทางตรงกลางที่เหมาะสมบน VPS

อิมเมจที่โฮสต์เองจำนวนมากไม่รองรับตัวแปร _FILE ดังนั้นตัวแปรสภาพแวดล้อมจึงเป็นช่องทางเดียวในการส่งค่าเข้าไป สำหรับ VPS ที่มีผู้ดูแลระบบเพียงคนเดียว เป้าหมายที่ทำได้จริงคือป้องกันไม่ให้ค่าต่างๆ ค้างอยู่ในไฟล์ที่ผู้ใช้ทุกคนอ่านได้ภายในไดเรกทอรีโครงการ และไม่นำค่าเหล่านั้นเข้า 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 จะสร้างไฟล์ด้วยโหมดที่กำหนดไว้แล้ว จึงไม่มีช่วงเวลาที่ผู้ใช้ทุกคนสามารถอ่านไฟล์ได้ root เป็นเจ้าของไฟล์ ดังนั้นผู้ใช้ที่ไม่ใช่ root บนเครื่องจะอ่านไฟล์นี้ไม่ได้ อย่างไรก็ตาม ผู้ที่สามารถเรียกใช้ docker ยังคงอ่านค่าจากคอนเทนเนอร์ได้ เพิ่ม *.env และ .env ลงใน .gitignore แล้ว commit ไฟล์ app.env.example ที่มีเฉพาะชื่อคีย์และค่าที่เว้นว่างแทน รหัสผ่านที่ถูก commit แล้วต้องเปลี่ยนใหม่

การเปลี่ยนค่าหมายถึงการเริ่มบริการใหม่ ตัวแปรสภาพแวดล้อมจะถูกอ่านเพียงครั้งเดียวเมื่อกระบวนการของคอนเทนเนอร์เริ่มทำงาน ดังนั้นการแก้ไขไฟล์จะไม่ส่งผลใดๆ จนกว่าจะเรียกใช้ docker compose up -d --force-recreate db รูปแบบเดียวกันนี้ใช้ในคู่มือ การตั้งค่า n8n หลัง HTTPS บน VPS ซึ่งเก็บคีย์การเข้ารหัสไว้นอกไฟล์ compose

แยกการตั้งค่าตามสภาพแวดล้อม

Compose จะอ่าน .env จากไดเรกทอรีโครงการตามค่าเริ่มต้น หากต้องการระบุที่อื่น ให้ใช้ --env-file

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

ระบบจะอ่านไฟล์หลายไฟล์ตามลำดับ และไฟล์หลังจะเขียนทับค่าจากไฟล์ก่อนหน้า เก็บค่าเริ่มต้นที่ไม่ใช่ความลับไว้ในไฟล์ที่ commit แล้ว และเก็บข้อมูลลับไว้ในไฟล์ที่ไม่เคยนำออกจากเซิร์ฟเวอร์ หลักการเดียวกันนี้ใช้กับ env_file: โดยไฟล์สุดท้ายที่ระบุจะมีผลเมื่อมีคีย์ซ้ำกัน

FAQ

เหตุใดไฟล์ .env ของฉันจึงถูกละเว้นภายในคอนเทนเนอร์

ไฟล์ดังกล่าวไม่ได้ถูกละเว้น ไฟล์ .env จะแทนที่เฉพาะตัวแทนตำแหน่ง ${NAME} ในไฟล์ compose เท่านั้น ไฟล์นี้ไม่เคยกำหนดตัวแปรภายในคอนเทนเนอร์ หากต้องการส่งค่าเข้าไปในคอนเทนเนอร์ ให้ระบุการอ้างอิงดังนี้: environment: { KEY: "${NAME}" } หรือใช้ env_file: ./that-file.env แทน

environment มีลำดับเหนือกว่า env_file หรือกลับกัน

environment: มีลำดับเหนือกว่า ลำดับที่ Docker ระบุไว้ให้แอตทริบิวต์ environment อยู่เหนือแอตทริบิวต์ env_file และทั้งสองรายการอยู่ต่ำกว่า docker compose run -e บนบรรทัดคำสั่ง หากกำหนดคีย์เดียวกันไว้ทั้งสองตำแหน่ง ค่าจาก env_file จะไม่ถูกใช้งานโดยไม่มีข้อความแจ้งเตือน

ฉันจะดูค่าท้ายสุดที่ Compose จะใช้ได้อย่างไร

เรียกใช้ docker compose config เพื่อแสดงไฟล์ compose ที่แก้ไขค่าครบถ้วนแล้ว พร้อมใช้การแทนค่าทั้งหมด หากคอนเทนเนอร์กำลังทำงานอยู่ docker inspect <container> --format '{{json .Config.Env}}' จะแสดงค่าที่โปรเซสได้รับอย่างตรงตามจริง

Compose secrets มีการเข้ารหัสหรือไม่

ไม่มี secret ที่อ้างอิงจากไฟล์จะถูกเมานต์เข้าไปในคอนเทนเนอร์เป็นไฟล์ข้อความธรรมดาที่ /run/secrets/<name> และไฟล์ต้นทางบนดิสก์ของ host ไม่ได้เข้ารหัส ประโยชน์คือการจำกัดขอบเขต ไม่ใช่การเข้ารหัส ค่า secret จะไม่อยู่ใน environment ของคอนเทนเนอร์ ไม่ปรากฏในเอาต์พุตของ docker inspect และไม่อยู่ใน crash dump ที่แสดง environment

ฉันสามารถใช้เครื่องหมายคำพูดและช่องว่างในไฟล์ env ได้หรือไม่

ใช้ KEY=value with spaces และไม่ต้องใส่เครื่องหมายคำพูด Compose จะถือว่าส่วนที่เหลือทั้งหมดของบรรทัดเป็นค่า ดังนั้นโดยทั่วไปเครื่องหมายคำพูดจะกลายเป็นอักขระจริงในค่า ห้ามเว้นวรรคโดยรอบ = เนื่องจากคีย์จะมีช่องว่างต่อท้าย และจะไม่ตรงกับรายการใดเลย