การใช้ Environment Variables และ .env ใน Docker Compose

การเขียน Password และ Secret ตรงๆ ใน docker-compose.yml เป็น Bad Practice ที่ต้องหลีกเลี่ยง โดยเฉพาะเมื่อต้องการ Commit โค้ดขึ้น Git เพราะ Credential จะถูกเปิดเผย วิธีที่ถูกต้องคือการใช้ Environment Variables และ .env File ซึ่งแยก Configuration ออกจากโค้ด และเป็น Best Practice ของ 12-Factor App ที่ DevOps ทุกคนควรรู้

Environment Variables คืออะไร?

Environment Variables คือตัวแปรที่กำหนดค่าให้ Process (Container) สามารถอ่านได้ขณะรัน ใน Docker Compose มี 3 วิธีหลักในการกำหนด Environment Variables:

  • Inline — เขียนตรงๆ ใน docker-compose.yml
  • env_file — อ่านจากไฟล์ .env แยกต่างหาก
  • Shell Environment — รับค่าจาก Environment ของ Host Machine

วิธีที่ 1: Inline (ไม่แนะนำสำหรับ Secret)

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword   # ❌ ไม่ควรทำ
      MYSQL_DATABASE: mydb
      MYSQL_USER: user
      MYSQL_PASSWORD: secret123           # ❌ ไม่ควรทำ

แม้จะง่าย แต่ไม่ควรใช้สำหรับ Password เพราะหากนำโค้ดขึ้น Git จะเปิดเผย Credential ทันที

วิธีที่ 2: .env File (แนะนำ)

สร้างไฟล์ .env ในโฟลเดอร์เดียวกับ docker-compose.yml:

# .env
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=wppassword
WP_PORT=8080

จากนั้นใช้ตัวแปรใน docker-compose.yml ด้วย Syntax ${VARIABLE_NAME}:

version: '3.8'

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - wp_network

  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    ports:
      - "${WP_PORT}:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
    networks:
      - wp_network

volumes:
  db_data:

networks:
  wp_network:

Docker Compose จะอ่านไฟล์ .env โดยอัตโนมัติ ไม่ต้องระบุอะไรเพิ่มเติม

วิธีที่ 3: env_file (กำหนด Path เอง)

หากต้องการใช้ไฟล์ชื่ออื่น หรือหลายไฟล์ ใช้ env_file:

services:
  db:
    image: mysql:8.0
    env_file:
      - ./config/database.env    # ไฟล์ .env ที่กำหนดเอง
      - ./config/common.env      # รวมหลายไฟล์ได้

ตัวอย่าง database.env:

MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=wppassword

ต้องเพิ่ม .env ใน .gitignore เสมอ

สิ่งสำคัญมากคือต้องป้องกันไม่ให้ไฟล์ .env ถูก Commit ขึ้น Git:

# .gitignore
.env
*.env
!.env.example    # ยกเว้น Template ไฟล์

แนะนำให้สร้าง .env.example เพื่อเป็น Template สำหรับ Developer คนอื่น:

# .env.example  (commit ไฟล์นี้ขึ้น Git ได้)
MYSQL_ROOT_PASSWORD=your_root_password_here
MYSQL_DATABASE=wordpress
MYSQL_USER=your_db_user_here
MYSQL_PASSWORD=your_db_password_here
WP_PORT=8080

Default Values และ Required Variables

Docker Compose รองรับ Syntax พิเศษสำหรับจัดการกรณีที่ตัวแปรไม่ถูกกำหนด:

services:
  app:
    environment:
      # ใช้ค่า Default ถ้า APP_PORT ไม่ถูกกำหนด
      PORT: ${APP_PORT:-3000}
      
      # Error ถ้า DB_HOST ไม่ถูกกำหนด (Required)
      DATABASE_URL: ${DB_HOST:?DB_HOST is required}
      
      # ใช้ค่าว่างถ้าไม่ถูกกำหนด (ไม่แนะนำสำหรับ Password)
      OPTIONAL_VAR: ${OPTIONAL_VAR:-}
Syntax ความหมาย
${VAR:-default} ใช้ค่า default ถ้า VAR ว่างหรือไม่กำหนด
${VAR-default} ใช้ค่า default ถ้า VAR ไม่กำหนด (แต่ยอมรับค่าว่าง)
${VAR:?error msg} หยุดทำงานพร้อม Error ถ้า VAR ว่างหรือไม่กำหนด
${VAR?error msg} หยุดทำงานพร้อม Error ถ้า VAR ไม่กำหนด

ตรวจสอบ Variables ก่อนรัน

ใช้คำสั่ง docker compose config เพื่อดูว่าตัวแปรถูกแทนค่าถูกต้องหรือไม่:

# แสดง Config ที่ถูก Interpolate แล้ว
docker compose config

# ตรวจสอบเฉพาะ Environment Variables
docker compose config | grep -A 20 environment

ลำดับความสำคัญของ Environment Variables

เมื่อมีการกำหนดตัวแปรหลายแหล่ง Docker Compose จะใช้ลำดับนี้ (สูงสุดก่อน):

  1. Shell Environment Variables ของ Host (ลำดับสูงสุด)
  2. ตัวแปรที่กำหนดใน docker-compose.yml โดยตรง
  3. ไฟล์ .env ในโฟลเดอร์โปรเจกต์
  4. ค่า Default ที่กำหนดใน Image (Dockerfile)

ตัวอย่าง: ถ้า Set WP_PORT=9090 ใน Shell แล้วรัน docker compose ขึ้นมา ค่าที่ใช้จะเป็น 9090 แทนที่ค่าใน .env

ตัวอย่างสมบูรณ์: WordPress Stack ด้วย .env

โครงสร้างไฟล์:

wordpress-stack/
├── docker-compose.yml
├── .env              # ← ไม่ Commit ขึ้น Git
├── .env.example      # ← Commit ขึ้น Git ได้
└── .gitignore

ไฟล์ .env:

MYSQL_ROOT_PASSWORD=SuperSecret123!
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=WpPass456!
WP_PORT=8080
MYSQL_PORT=3306

ไฟล์ docker-compose.yml:

version: '3.8'

services:
  db:
    image: mysql:8.0
    container_name: wordpress_db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD required}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-wordpress}
      MYSQL_USER: ${MYSQL_USER:?MYSQL_USER required}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:?MYSQL_PASSWORD required}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - wp_network

  wordpress:
    image: wordpress:latest
    container_name: wordpress_app
    restart: unless-stopped
    depends_on:
      - db
    ports:
      - "${WP_PORT:-8080}:80"
    environment:
      WORDPRESS_DB_HOST: db:${MYSQL_PORT:-3306}
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
    volumes:
      - wp_data:/var/www/html
    networks:
      - wp_network

volumes:
  db_data:
  wp_data:

networks:
  wp_network:

สรุป Best Practices

Best Practice เหตุผล
ใช้ .env แทนการ Hardcode แยก Config ออกจากโค้ด
เพิ่ม .env ใน .gitignore ป้องกัน Credential รั่วไหล
สร้าง .env.example ช่วย Developer คนอื่นตั้งค่า
ใช้ 😕 สำหรับ Required Vars Fail-Fast แทนที่จะรันด้วยค่าผิด
ใช้ :- สำหรับ Optional Vars มี Default ที่ปลอดภัย
รัน docker compose config ตรวจสอบก่อน Deploy

ขอแสดงความยินดี! คุณได้เรียนรู้ครบทุกบทความในชุด Docker Compose แล้ว ตั้งแต่พื้นฐานจนถึงการ Deploy Stack จริง บทความชุดถัดไปจะเป็นเรื่อง Reverse Proxy & SSL ซึ่งจะสอนให้คุณรัน Container บน Domain จริง พร้อม HTTPS โดยใช้ Nginx Proxy Manager และ Let’s Encrypt