رفتن به محتوا
LoopX

متغیرهای محیطی

توی این درس یاد می‌گیری چطور یک image واحد را برای محیط‌های مختلف تنظیم کنی، بدون اینکه آن را دوباره بسازی: متغیرهای محیطی (environment variables) را با -e و --env-file به کانتینر می‌دهی. می‌بینی image های رسمی (مثل MySQL) از چه متغیرهایی می‌خوانند (MYSQL_ROOT_PASSWORD و …)، چرا مقدار متغیرها در docker inspect دیده می‌شود (و چرا برای رازها کافی نیست)، و چرا تغییر متغیر نیاز به ساختن کانتینر جدید دارد.

متغیر محیطی مثل برچسب‌هایی روی در اتاق است که برنامه موقع شروع می‌خواند: «رمز دیتابیس این است»، «حالت: تست». یک اتاق (image) ثابت است ولی هر بار که برچسب‌های متفاوتی روی آن بچسبانی، رفتارش عوض می‌شود. برنامه‌هایی که با اصل «config از محیط می‌آید» نوشته شده‌اند (معروف به 12-factor app) برای هر محیط (توسعه، تست، production) فقط با متغیر تنظیم می‌شوند.

متغیر از فایل env یا خط فرمان به پروسه‌ی اصلی کانتینر می‌رسد؛ برنامه موقع شروع آن را می‌خواند.

مثال ۱: اولین متغیر با -e

Section titled “مثال ۱: اولین متغیر با -e”
Terminal window
docker run --rm -e GREETING=salam alpine sh -c 'echo "پیام: $GREETING"'
docker run --rm -e A=1 -e B=2 alpine env | grep -E '^(A|B)='
خروجی
پیام: salam
A=1
B=2

با -e نام=مقدار یک متغیر به کانتینر می‌دهی؛ می‌توانی چند -e پشت هم بنویسی. دستور env داخل کانتینر همه‌ی متغیرها را فهرست می‌کند و ما با grep فقط دو تایمان را بیرون کشیدیم.

مثال ۲: عبور از متغیر میزبان

Section titled “مثال ۲: عبور از متغیر میزبان”
Terminal window
export TOKEN=abc123
docker run --rm -e TOKEN alpine sh -c 'echo "داخل کانتینر: $TOKEN"'
docker run --rm alpine sh -c 'echo "بدون -e: [${TOKEN}]"'
خروجی
داخل کانتینر: abc123
بدون -e: []

اگر فقط اسم را بنویسی (-e TOKEN بدون مقدار)، داکر مقدارش را از محیط shell خودت برمی‌دارد. بدون -e هیچ متغیری از میزبان به کانتینر نمی‌رسد؛ کانتینر محیط جدا دارد.

وقتی متغیرها زیاد می‌شوند، آن‌ها را در یک فایل می‌گذاری:

Terminal window
cat > app.env <<'LXEOF'
# تنظیمات برنامه
APP_MODE=test
DB_HOST=db.local
DB_PORT=5432
LXEOF
docker run --rm --env-file app.env alpine env | grep -E '^(APP_|DB_)'
خروجی
APP_MODE=test
DB_HOST=db.local
DB_PORT=5432

قاعده‌ی فایل: هر خط KEY=VALUE، خط‌های # توضیح‌اند و خط‌های خالی نادیده گرفته می‌شوند. گیومه در فایل env معنی ندارد و جزو مقدار می‌شود (اشتباه ۲ را ببین). فایل را با .gitignore از git دور نگه دار، چون ممکن است رمز داشته باشد.

مثال ۴: image های رسمی از متغیر می‌خوانند (MYSQL_ROOT_PASSWORD)

Section titled “مثال ۴: image های رسمی از متغیر می‌خوانند (MYSQL_ROOT_PASSWORD)”

image رسمی MySQL موقع شروع رمز root و تنظیمات اولیه را از متغیرها می‌خواند. اول ساده‌ترین شکل (همان دستور سرفصل درس):

Terminal window
docker run -d --name lx-e1 -e MYSQL_ROOT_PASSWORD=secret mysql:8.4 >/dev/null
for i in $(seq 1 40); do docker logs lx-e1 2>&1 | grep -q "ready for connections.*port: 3306" && break; sleep 2; done
docker exec -e MYSQL_PWD=secret lx-e1 mysql -uroot -N -e "SELECT 'اتصال با رمز secret موفق بود'" 2>&1 | grep -v Warning
docker rm -f lx-e1 >/dev/null
خروجی
اتصال با رمز secret موفق بود

حالا با فایل env و چند متغیر (تنظیم پایگاه‌داده و کاربر اختصاصی). متغیرهای رسمی این image: MYSQL_ROOT_PASSWORD (رمز root)، MYSQL_DATABASE (پایگاه‌داده‌ای که در شروع ساخته شود)، MYSQL_USER و MYSQL_PASSWORD (یک کاربر معمولی):

Terminal window
cat > db.env <<'LXEOF'
MYSQL_ROOT_PASSWORD=rootpass
MYSQL_DATABASE=shop
MYSQL_USER=app
MYSQL_PASSWORD=apppass
LXEOF
docker run -d --name lx-e2 --env-file db.env mysql:8.4 >/dev/null
for i in $(seq 1 40); do docker logs lx-e2 2>&1 | grep -q "ready for connections.*port: 3306" && break; sleep 2; done
docker exec -e MYSQL_PWD=apppass lx-e2 mysql -uapp -N -e "SHOW DATABASES" 2>&1 | grep -v Warning
خروجی
information_schema
performance_schema
shop

کاربر app با رمز apppass وارد شد و پایگاه‌داده‌ی shop را می‌بیند (که فقط با متغیر MYSQL_DATABASE ساخته شد). هیچ دستور SQL ای ننوشتیم؛ فقط متغیر دادیم. این همان قدرت image های رسمی است؛ برای هر image فهرست متغیرها در صفحه‌ی Docker Hub آن آمده.

⚡ بررسی سریع

رمز root در image رسمی MySQL با کدام متغیر تنظیم می‌شود؟

مثال ۵: متغیرهای پیش‌فرض image و override

Section titled “مثال ۵: متغیرهای پیش‌فرض image و override”

image ها خودشان متغیر دارند (که سازنده با ENV در Dockerfile گذاشته). -e آن‌ها را بازنویسی می‌کند:

Terminal window
docker run --rm nginx:alpine env | grep NGINX_VERSION
docker run --rm -e NGINX_VERSION=fake nginx:alpine env | grep NGINX_VERSION
خروجی
NGINX_VERSION=1.31.6
NGINX_VERSION=fake

مثال ۶: متغیرها از نگاه سیستم

Section titled “مثال ۶: متغیرها از نگاه سیستم”
Terminal window
docker run -d --name lx-e3 -e SECRET_KEY=abc123 alpine sleep 120 >/dev/null
docker exec lx-e3 sh -c 'cat /proc/1/environ | tr "\0" "\n" | grep SECRET_KEY'
docker inspect lx-e3 --format '{{range .Config.Env}}{{println .}}{{end}}' | grep SECRET_KEY
docker rm -f lx-e3 >/dev/null
خروجی
SECRET_KEY=abc123
SECRET_KEY=abc123

متغیرهای محیطی در environment پروسه‌ی شماره ۱ هستند (فایل مجازی /proc/1/environ) و در پیکربندی کانتینر هم ثبت می‌شوند؛ پس هر کس به docker inspect یا docker exec دسترسی دارد، مقدارشان را می‌بیند. نتیجه‌ی امنیتی مهم: متغیر محیطی برای رازهای حساس (رمز production) راه ایده‌آل نیست؛ در درس «امنیت پایه» با روش‌های بهتر آشنا می‌شوی.

متغیرهای محیطی ساده‌ترین مکانیزم یونیکسی برای انتقال تنظیمات به یک پروسه‌اند: موقع شروع پروسه (exec) یک فهرست KEY=VALUE همراه آن می‌شود. داکر همین فهرست را از سه منبع می‌سازد، به این ترتیب اولویت:

  1. ENV در Dockerfile (پیش‌فرض image)
  2. --env-file
  3. -e (بالاترین اولویت؛ آخرین مقدار برنده است)

برنامه‌ی داخل کانتینر با getenv (یا process.env، os.environ) آن‌ها را می‌خواند. چون متغیرها فقط موقع شروع خوانده می‌شوند، عوض کردن آن‌ها روی کانتینر موجود ممکن نیست (اشتباه ۴).

شکل معنی
-e KEY=value یک متغیر با مقدار مشخص
-e KEY مقدار را از shell میزبان بردار
--env-file FILE چند متغیر از فایل
متغیر image کار
MYSQL_ROOT_PASSWORD mysql رمز root (الزامی)
MYSQL_DATABASE mysql ساخت پایگاه‌داده‌ی اولیه
MYSQL_USER / MYSQL_PASSWORD mysql ساخت کاربر معمولی

قواعد فایل env: KEY=VALUE در هر خط؛ # توضیح؛ بدون export؛ گیومه جزو مقدار است؛ فاصله‌ی دور = نمی‌گذاریم.

۱) فراموش کردن متغیر الزامی

Section titled “۱) فراموش کردن متغیر الزامی”
Terminal window
docker run --name lx-bad mysql:8.4
docker rm -f lx-bad >/dev/null
خروجی
2026-10-03 10:30:33+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.4.11-1.el9 started.
2026-10-03 10:30:33+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2026-10-03 10:30:33+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.4.11-1.el9 started.
2026-10-03 10:30:34+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
You need to specify one of the following as an environment variable:
- MYSQL_ROOT_PASSWORD
- MYSQL_ALLOW_EMPTY_PASSWORD
- MYSQL_RANDOM_ROOT_PASSWORD

image MySQL بدون MYSQL_ROOT_PASSWORD (یا یکی از دو گزینه‌ی دیگر که پیام می‌گوید) اجرا نمی‌شود و بلافاصله خارج می‌شود. راه‌حل: همیشه صفحه‌ی Docker Hub یا docker logs را بخوان؛ پیام خطا دقیقاً می‌گوید چه متغیری لازم است.

Terminal window
printf 'NAME="ali"\n' > quote.env
docker run --rm --env-file quote.env alpine sh -c 'echo "مقدار: [$NAME]"'
خروجی
مقدار: ["ali"]

گیومه‌ها جزو مقدار شدند ("ali" با گیومه). راه‌حل: در فایل env بدون گیومه بنویس: NAME=ali.

۳) فاصله در مقدار روی خط فرمان

Section titled “۳) فاصله در مقدار روی خط فرمان”
Terminal window
docker run --rm -e MSG=hello world alpine echo ok
docker run --rm -e "MSG=hello world" alpine sh -c 'echo "[$MSG]"'
خروجی
Unable to find image 'world:latest' locally
docker: Error response from daemon: pull access denied for world, repository does not exist or may require 'docker login'
Run 'docker run --help' for more information
[hello world]

بدون گیومه، world مثل یک دستور جداگانه (نام image) تعبیر شد و خطا گرفتی. راه‌حل: کل KEY=مقدار با فاصله را در گیومه بگذار.

۴) تغییر متغیر روی کانتینر موجود

Section titled “۴) تغییر متغیر روی کانتینر موجود”
Terminal window
docker run -d --name lx-e3 -e MODE=old alpine sleep 120 >/dev/null
docker stop -t 1 lx-e3 >/dev/null; docker start lx-e3 >/dev/null
docker exec lx-e3 sh -c 'echo "MODE=$MODE"'
docker rm -f lx-e3 >/dev/null
خروجی
MODE=old

حتی بعد از stop و start متغیر همان است. راهی برای عوض کردن متغیرهای یک کانتینر موجود نیست. راه‌حل: کانتینر را حذف و با -e جدید دوباره بساز (در Compose با up -d که آن را خودکار می‌سازد).

۵) گذاشتن رمز واقعی در فایل و commit کردنش

Section titled “۵) گذاشتن رمز واقعی در فایل و commit کردنش”

فایل .env را به git اضافه نکن. راه‌حل: .gitignore و یک فایل نمونه‌ی بی‌رمز (.env.example).

✎ تمرینآسان

یک کانتینر alpine بساز که با دو متغیر NAME=ali و AGE=30 جمله‌ی «ali is 30» را چاپ کند.

دیدن جواب
Terminal window
docker run --rm -e NAME=ali -e AGE=30 alpine sh -c 'echo "$NAME is $AGE"'
خروجی
ali is 30
✎ تمرینمتوسط

یک فایل web.env با دو متغیر COLOR=blue و SIZE=large بساز و با --env-file آن را به یک کانتینر بده؛ ثابت کن هر دو متغیر آنجا هستند.

دیدن جواب
Terminal window
printf 'COLOR=blue\nSIZE=large\n' > web.env
docker run --rm --env-file web.env alpine env | grep -E '^(COLOR|SIZE)='
خروجی
COLOR=blue
SIZE=large
✎ تمرینسخت

تمرین اصلی: یک دیتابیس MySQL با تنظیمات از فایل env اجرا کن (MYSQL_ROOT_PASSWORD=pw، MYSQL_DATABASE=blog). ثابت کن پایگاه‌داده‌ی blog ساخته شده، بعد با docker inspect نشان بده رمز در پیکربندی دیده می‌شود، و در آخر کانتینر را حذف کن.

دیدن جواب
Terminal window
printf 'MYSQL_ROOT_PASSWORD=pw\nMYSQL_DATABASE=blog\n' > blog.env
docker run -d --name lx-e1 --env-file blog.env mysql:8.4 >/dev/null
for i in $(seq 1 40); do docker logs lx-e1 2>&1 | grep -q "ready for connections.*port: 3306" && break; sleep 2; done
docker exec -e MYSQL_PWD=pw lx-e1 mysql -uroot -N -e "SHOW DATABASES LIKE 'blog'" 2>&1 | grep -v Warning
docker inspect lx-e1 --format '{{range .Config.Env}}{{println .}}{{end}}' | grep MYSQL_ROOT_PASSWORD
docker rm -f lx-e1 >/dev/null
خروجی
blog
MYSQL_ROOT_PASSWORD=pw

دیدی که رمز در docker inspect آشکار است؛ برای همین برای رازهای واقعی روش‌های امن‌تر لازم است.

؟ آزمونک
  1. متغیر محیطی چه کمکی می‌کند؟

  2. در -e TOKEN (بدون مقدار) مقدار از کجا می‌آید؟

  3. گیومه در فایل --env-file چه می‌شود؟

  4. آیا متغیرهای محیطی کانتینر در docker inspect دیده می‌شوند؟

  5. چطور متغیرهای یک کانتینر موجود را عوض می‌کنی؟

  • -e KEY=value یا -e KEY (از shell) و --env-file FILE متغیر محیطی را به کانتینر می‌دهند.
  • image های رسمی از متغیرها می‌خوانند (MySQL: MYSQL_ROOT_PASSWORD، MYSQL_DATABASE، MYSQL_USER، MYSQL_PASSWORD).
  • در فایل env: KEY=VALUE، بدون گیومه؛ # توضیح.
  • متغیرها در docker inspect و /proc/1/environ دیده می‌شوند؛ برای رازها کافی نیستند.
  • متغیر کانتینر موجود عوض نمی‌شود؛ کانتینر را دوباره بساز.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
docker run -e KEY=value IMGیک متغیر
docker run -e KEY IMGمقدار از shell میزبان
docker run --env-file .env IMGمتغیرها از فایل
docker run -e MYSQL_ROOT_PASSWORD=secret mysqlرمز root برای MySQL
docker exec N envمتغیرهای کانتینر
docker inspect --format "{{.Config.Env}}" Nمتغیرها در پیکربندی
printf "KEY=value\n" > .envساختن فایل env