ข้ามไปยังเนื้อหา

Env Vars, Volumes และ Networks

มีสองวิธีในการส่ง environment variables เข้า service

กำหนด variables โดยตรงใน compose.yaml:

services:
app:
image: myapp:latest
environment:
- NODE_ENV=production
- PORT=3000

อ้างอิงไฟล์ภายนอก Compose อ่านแต่ละบรรทัด KEY=VALUE จากไฟล์และฉีดเข้า container:

services:
app:
image: myapp:latest
env_file:
- .env

ไฟล์ .env:

NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://db:5432/mydb

เก็บ .env ออกจาก version control — มักมี secrets อยู่

Compose อ่านไฟล์ .env ใน directory เดียวกับ compose.yaml แล้วเอาค่าจากไฟล์นั้นไปแทนที่ placeholder ${VAR} ภายใน compose.yaml เอง:

services:
db:
image: postgres:17-alpine
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_NAME}

ด้วยไฟล์ .env ที่มี:

DB_PASSWORD=supersecret
DB_NAME=mydb

Compose แทนที่ค่าก่อนสตาร์ท container นี่ทำให้ secrets ไม่ต้องอยู่ใน compose.yaml และให้นักพัฒนาแต่ละคนใช้ไฟล์ .env ของตนเองได้

container เป็น ephemeral — ข้อมูลที่เขียนภายใน container จะหายเมื่อ container ถูกลบ Named volumes เก็บข้อมูลบน host และอยู่รอดได้แม้ container จะ restart หรือถูก recreate

ประกาศ named volume ที่ระดับบนสุดและ mount ต่อ service:

services:
db:
image: postgres:17-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=secret
volumes:
pgdata:

key volumes: ระดับบนสุด register volume กับ Compose ส่วนรายการ volumes: ระดับ service เป็นตัว mount volume นั้นเข้าไป คุณสามารถ mount named volume เดียวกันในหลาย service ได้

Compose สร้าง default network อัตโนมัติ สำหรับการควบคุมมากขึ้น — แยก services หรือเชื่อมต่อ services ข้าม Compose files — กำหนด custom networks:

services:
frontend:
image: nginx:alpine
networks:
- public
backend:
image: myapp:latest
networks:
- public
- private
db:
image: postgres:17-alpine
networks:
- private
networks:
public:
private:

ที่นี่ frontend เข้าถึง backend ได้ แต่ frontend ไม่สามารถเข้าถึง db โดยตรง backend เชื่อม network ทั้งสอง

snippet ด้านล่างเชื่อมต่อ service กับ env file และ named volume เพื่อให้ข้อมูลอยู่รอดได้หลัง restart

# Create .env file
cat > .env <<'EOF'
APP_MSG=Hello from .env interpolation!
VOLUME_DATA_DIR=/data
EOF

# Create compose.yaml with env interpolation and a named volume
cat > compose.yaml <<'EOF'
services:
  writer:
    image: alpine
    env_file:
      - .env
    volumes:
      - mydata:${VOLUME_DATA_DIR}
    command: >
      sh -c "
        echo ${APP_MSG} &&
        echo 'Writing to named volume...' &&
        echo 'persistent data' > ${VOLUME_DATA_DIR}/file.txt &&
        echo 'Wrote file to volume.'
      "

  reader:
    image: alpine
    volumes:
      - mydata:/data
    command: >
      sh -c "
        sleep 2 &&
        echo 'Reading from volume:' &&
        cat /data/file.txt
      "
    depends_on:
      - writer

volumes:
  mydata:
EOF

# Run both services
docker compose up

# Verify the volume still exists after containers stop
docker volume ls | grep mydata

# Clean up (keeps the volume)
docker compose down

# Clean up including the volume
docker compose down -v
ตัวเลือกBenefitCost
.env + interpolationsecrets ไม่ต้อง hardcode ใน compose.yaml นักพัฒนาแต่ละคนใช้ค่าตัวเองได้ต้องดูแลไฟล์ .env เองนอก git และ sync key ให้ตรงกับที่ compose.yaml อ้างอิง
named volume + custom networkข้อมูลอยู่รอดข้าม restart และแยก traffic ตาม network ได้ชัดเจนvolume/network ผูกกับชื่อ project — ถ้าตั้งชื่อโปรเจกต์ชนกัน อาจ mount ข้อมูลผิดตัว
  • commit ไฟล์ .env ที่มี secret จริง (เช่น DB_PASSWORD) เข้า git โดยไม่ได้ตั้งใจ ควรใส่ .env ใน .gitignore เสมอและ commit แค่ .env.example
  • ลืมว่า named volume และ network ที่ Compose สร้างให้เป็นแบบ project-scoped (ตั้งชื่อจาก directory) โปรเจกต์ที่ชื่อคล้ายกันอาจสร้าง volume ซ้ำซ้อนโดยไม่รู้ตัว
  • สับสนระหว่าง environment: กับ env_file: แล้วคาดหวังว่าไฟล์ .env จะถูกอ่านเข้า container เองโดยไม่ต้องประกาศ env_file: ทั้งที่ interpolation กับ env_file เป็นคนละกลไกกัน

💡 ตัวอย่างจากของจริง

ทีมที่ทำ CI/CD มักแยก .env.example (commit เข้า git เพื่อ document ว่า key ไหนต้องตั้งค่า) ออกจาก .env จริงที่เก็บ secret และฉีดเข้าเครื่องผ่าน secret manager หรือ CI variable แทน — pattern เดียวกับที่ทีม dev ใช้ Compose local แล้วส่ง secret จริงผ่าน Kubernetes Secret ตอน deploy production

ความแตกต่างระหว่าง environment: และ env_file: ในนิยาม service คืออะไร?
Compose มองหาค่า interpolation ${VAR} ที่ไหน?
เกิดอะไรขึ้นกับ named volume เมื่อรัน docker compose down (ไม่มี -v)?
ในตัวอย่าง networks ทำไม frontend จึงไม่สามารถเข้าถึง db โดยตรง?