SSD Nodes Learn 8GB RAM — $66/سال
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-01

Docker Compose میں .env، env_file اور secrets کا فرق

Docker Compose میں .env، env_file اور environment کے فرق، precedence کے اصول، اور secrets رکھنے کی درست جگہ جانیں، تاکہ passwords غیر محفوظ نہ ہوں۔

لوگ جنہیں لوگ env فائل کہتے ہیں، ان کی تین اقسام

Docker Compose میں تین الگ طریقۂ کار ہیں جن کے نام ایک دوسرے سے ملتے جلتے ہیں اور اس وجہ سے الجھن پیدا ہوتی ہے۔ .env فائل، Compose کے فائل کو parse کرنے سے پہلے ہی، خود compose.yaml کے اندر موجود ${VARIABLE} placeholders کی جگہ قدریں درج کرتی ہے۔ env_file: attribute، key/value جوڑوں والی فائل کو container کے environment میں load کرتا ہے۔ environment: attribute، compose فائل میں براہِ راست لکھے گئے variables کو container پر set کرتا ہے۔ یہ ایک دوسرے کا متبادل نہیں ہیں۔ اگر ان میں سے دو ایک ہی key set کریں، تو نتیجہ documented precedence order کے مطابق طے ہوتا ہے۔

یہ guide ہر طریقے کو عملی طور پر دکھاتی ہے۔ یہ ایک ایسی command کے ذریعے precedence ثابت کرتی ہے جسے آپ چلا سکتے ہیں۔ پھر یہ زیادہ اہم نکتے کی وضاحت کرتی ہے: جو بھی docker inspect چلا سکتا ہے، وہ environment variables پڑھ سکتا ہے۔ اس لیے passwords کو ان میں شامل نہیں کرنا چاہیے۔ اگر آپ compose فائلوں سے عمومی طور پر نئے ہیں، تو پہلے VPS پر Docker Compose کی بنیادی باتیں پڑھیں، پھر configuration کے لیے یہاں واپس آئیں۔

.env فائل compose فائل کے لیے ہے، container کے لیے نہیں

ایک directory بنائیں اور اس میں دو files رکھیں۔

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

اب Compose سے پوچھیں کہ اس نے حقیقت میں کیا parse کیا ہے۔

docker compose config

آؤٹ پٹ میں image: alpine:3.20 دکھائی دیتا ہے۔ Placeholder ختم ہو چکا ہے، کیونکہ interpolation parse کے وقت ہوئی تھی۔ Compose project directory میں .env تلاش کرتا ہے۔ یہ وہ directory ہے جس میں compose file موجود ہے۔ پھر اسے ملنے والی ہر ${NAME} کو تبدیل کر دیتا ہے۔

اب service چلائیں۔

docker compose run --rm demo

printenv ALPINE_TAG status 1 کے ساتھ بند ہو جاتا ہے اور کچھ بھی print نہیں کرتا۔ یہ variable container کے اندر موجود نہیں ہے۔ یہی سب سے عام غلط فہمی ہے: .env نے compose file کو configure کیا تھا، process کو نہیں۔ اگر .env file میں POSTGRES_PASSWORD=hunter2 موجود ہو، تو database کے لیے اس کا کوئی اثر نہیں ہوتا، جب تک compose file کا کوئی حصہ اس کا حوالہ نہ دے۔

${NAME:-default} اس وقت fallback value فراہم کرتا ہے جب variable unset یا empty ہو۔ ${NAME:?message} Compose کو start ہونے سے روک کر آپ کا پیغام print کرتا ہے۔ ایسی value کے لیے یہی درست انتخاب ہے جس کا کوئی محفوظ default موجود نہ ہو۔

env_file کنٹینر میں متغیرات لوڈ کرتا ہے

env_file: attribute ایک یا زیادہ فائلوں کے نام متعین کرتا ہے۔ ان فائلوں کا مواد کنٹینر کے environment variables بن جاتا ہے۔

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 لائنوں پر مشتمل ہوتا ہے، ہر لائن الگ ہوتی ہے، اور # سے comment شروع ہوتا ہے۔ یہ shell نہیں ہے۔ زیادہ تر صورتوں میں quotes قدر کا حصہ رہتے ہیں، اور export prefixes کی ضرورت نہیں ہوتی۔ = sign کے اردگرد spaces نہ رکھیں، کیونکہ KEY = value ایسی variable بناتا ہے جس کا نام لفظی طور پر KEY ہوتا ہے اور اس کی قدر کے شروع میں ایک leading space شامل ہوتی ہے۔

اگر env_file path موجود نہ ہو تو یہ error ہے اور Compose رک جاتا ہے۔ اگر فائل کا موجود نہ ہونا قابل قبول ہو تو اسے optional قرار دیں:

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

environment متغیرات کو inline طور پر متعین کرتا ہے

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

دو syntaxes قبول کیے جاتے ہیں: اوپر دیا گیا mapping فارم اور - GREETING=from_environment استعمال کرنے والا list فارم۔ دونوں ایک ہی طرح کام کرتے ہیں۔ list فارم میں ایک اضافی سہولت ہے: بغیر value والی bare key، اس variable کو اس shell سے منتقل کرتی ہے جہاں آپ نے docker compose چلایا تھا۔

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

یہ from_my_shell دکھاتا ہے۔ اگر shell میں GREETING متعین کیے بغیر اسے چلائیں تو Compose کچھ متعین نہیں کرتا اور کوئی warning بھی نہیں دیتا۔ خاموشی سے ہونے والی pass-through ناکامیوں سے آگاہ رہنا ضروری ہے، کیونکہ empty password variable کے ساتھ شروع ہونے والی service عموماً کامیابی سے شروع ہو جاتی ہے اور اس کا access مکمل طور پر کھلا رہتا ہے۔

کون سی قدر غالب آتی ہے

Docker ترجیح کی ترتیب بیان کرتا ہے، جس میں سب سے زیادہ ترجیح پہلے آتی ہے: کمانڈ لائن پر docker compose run -e، پھر environment یا env_file، جن کی قدر آپ کے shell یا env فائل سے interpolated ہوتی ہے، پھر compose فائل میں موجود سادہ environment، اس کے بعد env_file، اور آخر میں image میں شامل ENV directive۔

روزمرہ کے کام کے لیے مختصر اصول یہ ہے: 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 میں موجود قدر کو override کر دیا۔ دوسرا کمانڈ from_cli دکھاتا ہے۔ compose فائل میں موجود کوئی قدر کمانڈ لائن کو override نہیں کرتی۔

جب کوئی container اس طرح کام کر رہا ہو جیسے آپ کی configuration لاگو ہی نہ ہوئی ہو، تو اندازہ نہ لگائیں۔ docker compose config مکمل طور پر resolved فائل دکھاتا ہے، جبکہ docker compose config --environment وہ interpolation variables دکھاتا ہے جن سے Compose کام کر رہا ہے۔ "میری env فائل نظرانداز ہو رہی ہے" کی زیادہ تر رپورٹس میں معلوم ہوتا ہے کہ ایک ہی قدر دو مختلف سطحوں پر مقرر کی گئی تھی۔

ماحول کے متغیرات کیوں افشا ہوتے ہیں

environment: میں پاس ورڈ مقرر کریں، تو یہ ڈسک پر container کی configuration میں محفوظ ہو جاتا ہے اور docker گروپ کا ہر صارف اسے دیکھ سکتا ہے۔

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

آؤٹ پٹ میں "DB_PASSWORD=hunter2" سادہ متن میں شامل ہوتا ہے۔ مزید تین paths بھی یہی value ظاہر کرتے ہیں۔ docker compose config اسے terminal پر دکھاتا ہے، جس کے نتیجے میں یہ support forum میں paste ہو جاتا ہے۔ container کے اندر کوئی بھی process /proc/1/environ کو پڑھ سکتا ہے، اور ہر child process اس variable کو inherit کرتا ہے۔ اس کے علاوہ، application کے crash handlers معمول کے مطابق پورے environment کو log یا error report میں dump کر دیتے ہیں۔

docker گروپ کی رکنیت host پر عملاً root کے برابر ہوتی ہے، اس لیے اسے ایسی privilege boundary نہ سمجھیں جس پر انحصار کیا جا سکے۔ VPS پر کم سے کم مراعات والے صارف اکاؤنٹس کی guide وضاحت کرتی ہے کہ کسی بھی shared box پر اس گروپ کی رسائی محدود رکھنا کیوں ضروری ہے۔

Compose secrets کی قدر فائل میں رکھتے ہیں

Compose فائل پر مبنی secrets کو سپورٹ کرتا ہے۔ قدر کو environment میں شامل کرنے کے بجائے container کے اندر ایک فائل کے طور پر mount کیا جاتا ہے۔

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

secret کو container کے اندر /run/secrets/db_password پر mount کیا جاتا ہے۔ slash کے بعد کا نام top-level secrets: block سے secret کا نام ہوتا ہے۔

_FILE suffix Docker Official Images، بشمول postgres، mysql اور mariadb، میں استعمال ہونے والا ایک convention ہے۔ ان کے entrypoint scripts VARNAME_FILE کو تلاش کرتے ہیں، فائل پڑھتے ہیں، اور اس کے contents استعمال کرتے ہیں۔ یہ Docker کی feature نہیں ہے، اس لیے یہ صرف وہاں کام کرتا ہے جہاں image اسے implement کرتی ہو۔ یہ فرض کرنے سے پہلے کہ SOMETHING_FILE کو تسلیم کیا جائے گا، image کی documentation دیکھیں۔ جو applications اسے سپورٹ نہیں کرتیں، وہ عموماً startup کے وقت خود فائل پڑھ سکتی ہیں۔ بصورت دیگر آپ path منتقل کر سکتے ہیں اور اپنا entrypoint یہ کام انجام دے سکتا ہے۔

چلتے ہوئے container کے اندر سے تصدیق کریں:

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

پہلی command password دکھاتی ہے۔ دوسری کچھ نہیں دکھاتی، کیونکہ قدر environment میں کبھی شامل نہیں ہوئی۔ یہی اس طریقے کا مقصد ہے: اس container میں docker inspect صرف غیر حساس path دکھاتا ہے۔

Host پر source file کو محفوظ رکھیں، کیونکہ secret کی رازداری اس کے پیچھے موجود فائل جتنی ہی ہوتی ہے:

chmod 600 db_password.txt

VPS پر عملی درمیانی راستہ

بہت سی خود میزبان کردہ images _FILE variables کو سپورٹ نہیں کرتیں، اس لیے داخل ہونے کا واحد طریقہ environment variables ہیں۔ ایک administrator کے زیر انتظام واحد VPS پر حقیقت پسندانہ مقصد یہ ہے کہ values آپ کے project directory میں موجود ایسی file میں نہ رہیں جسے ہر صارف پڑھ سکتا ہو، اور انہیں 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 file کو پہلے سے مقررہ mode کے ساتھ بناتا ہے، اس لیے کوئی ایسا وقفہ نہیں ہوتا جس میں اسے ہر صارف پڑھ سکے۔ اس کا مالک root ہوتا ہے، اس لیے machine پر موجود non root user اسے نہیں پڑھ سکتا، تاہم جو بھی docker چلا سکتا ہو وہ container سے value پھر بھی پڑھ سکتا ہے۔ *.env اور .env کو .gitignore میں شامل کریں، اور اس کے بجائے ایک app.env.example commit کریں جس میں key names ہوں اور values خالی ہوں۔ Commit کیا گیا password تبدیل کرنا ضروری ہے۔

کسی value کو تبدیل کرنے کے لیے service کو restart کرنا ہوتا ہے۔ Environment variables کو container process شروع ہوتے وقت صرف ایک بار پڑھا جاتا ہے، اس لیے file میں ترمیم کرنے سے کچھ تبدیل نہیں ہوتا جب تک آپ docker compose up -d --force-recreate db نہ چلائیں۔ یہی طریقہ VPS پر HTTPS کے پیچھے n8n guide میں استعمال کیا گیا ہے، جہاں encryption key compose file سے باہر رکھی جاتی ہے۔

ہر ماحول کے لیے کنفیگریشن تقسیم کرنا

Compose پہلے سے پروجیکٹ ڈائریکٹری سے .env پڑھتا ہے۔ --env-file کے ذریعے اسے کسی دوسری جگہ کی طرف متعین کریں۔

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

متعدد فائلیں ترتیب کے مطابق پڑھی جاتی ہیں، اور بعد والی فائلیں پہلے والی فائلوں کی ترتیبات کو override کرتی ہیں۔ غیر خفیہ ڈیفالٹس کو committed فائل میں رکھیں، جبکہ secrets کو ایسی فائل میں رکھیں جو کبھی server سے باہر نہ جائے۔ یہی اصول env_file: پر بھی لاگو ہوتا ہے؛ تکراری key کی صورت میں آخری فہرست شدہ فائل کی قدر مؤثر ہوتی ہے۔

FAQ

میری .env فائل container کے اندر نظر انداز کیوں ہو جاتی ہے؟

اسے نظر انداز نہیں کیا جاتا۔ .env فائل صرف compose فائل میں موجود ${NAME} placeholders کو تبدیل کرتی ہے۔ یہ کبھی بھی container کے اندر variables متعین نہیں کرتی۔ value کو container میں پہنچانے کے لیے اس کا حوالہ دیں: environment: { KEY: "${NAME}" }، یا اس کے بجائے env_file: ./that-file.env استعمال کریں۔

کیا environment، env_file کو override کرتا ہے، یا اس کے برعکس ہوتا ہے؟

environment: کو ترجیح حاصل ہے۔ Docker کی دستاویزی ترجیحی ترتیب میں environment attribute، env_file attribute سے اوپر ہوتا ہے، اور دونوں command line پر موجود docker compose run -e سے نیچے ہوتے ہیں۔ اگر کوئی key دونوں جگہ متعین ہو، تو env_file میں موجود value خاموشی سے غیر استعمال شدہ رہتی ہے۔

Compose کی استعمال کی جانے والی حتمی value کیسے دیکھوں؟

تمام interpolation لاگو ہونے کے بعد مکمل طور پر حل شدہ compose فائل دکھانے کے لیے docker compose config چلائیں۔ پہلے سے چل رہے container کے لیے docker inspect <container> --format '{{json .Config.Env}}' بالکل وہ value دکھاتا ہے جو اس کے process کو موصول ہوئی۔

کیا Compose secrets encrypted ہوتے ہیں؟

نہیں۔ file based secret، /run/secrets/<name> پر container میں plain file کے طور پر mount ہوتا ہے، اور source file host کی disk پر unencrypted رہتی ہے۔ اس کا فائدہ encryption نہیں بلکہ scope ہے: value container environment سے باہر، docker inspect output سے باہر، اور ان crash dumps سے باہر رہتی ہے جو environment کو print کرتے ہیں۔

کیا میں env فائل میں quotes اور spaces استعمال کر سکتا ہوں؟

KEY=value with spaces استعمال کریں اور quotes نہ لگائیں۔ Compose پوری باقی line کو value سمجھتا ہے، اس لیے quotes عموماً value میں literal characters کے طور پر شامل ہو جاتے ہیں۔ = کے اردگرد spaces کبھی نہ رکھیں، کیونکہ اس صورت میں key کے آخر میں space شامل ہو جاتی ہے اور کوئی چیز اس سے match نہیں کرتی۔