SSD Nodes Learn 8GB RAM — $66/سنة
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-01

ملفات env والأسرار في Docker Compose: الفرق والأولوية

افهم الفرق بين .env وenv_file وenvironment في Docker Compose، واعرف أيها يفوز عند التعارض ولماذا لا تضع كلمات المرور في متغيرات البيئة.

ثلاثة أشياء يُطلق عليها اسم ملف env

يستخدم Docker Compose ثلاث آليات منفصلة تحمل أسماء متشابهة بشكل مربك. يملأ ملف .env العناصر النائبة ${VARIABLE} داخل compose.yaml نفسه، قبل أن يحلل Compose الملف. وتحمل السمة env_file: ملفًا يحتوي على أزواج من المفاتيح والقيم إلى بيئة الحاوية. وتعيّن السمة environment: المتغيرات مباشرةً على الحاوية، وتكون مكتوبة في ملف compose. هذه الآليات غير قابلة للتبادل. وعندما تعيّن اثنتان منها المفتاح نفسه، تحدد الأولوية بينهما قاعدة أسبقية موثقة.

يوضح هذا الدليل عمل كل آلية، ويثبت الأسبقية باستخدام أمر يمكنك تشغيله، ثم يغطي النقطة الأهم: يمكن لأي شخص يستطيع تشغيل docker inspect قراءة متغيرات البيئة، لذلك لا تضع كلمات المرور فيها. إذا كنت جديدًا على ملفات compose عمومًا، فابدأ بـ أساسيات Docker Compose على VPS ثم عد إلى هنا لمعرفة المزيد عن الإعدادات.

ملف .env مخصص لملف Compose، وليس للحاوية

أنشئ دليلًا وضع فيه ملفين.

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} يجده.

شغّل الخدمة بعد ذلك.

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: ملفًا واحدًا أو عدة ملفات تصبح محتوياتها متغيرات بيئة داخل الحاوية.

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 عادية، سطر واحد لكل متغير، ويبدأ التعليق بالرمز #. هذا ليس ملف shell. تبقى علامات الاقتباس جزءًا من القيمة في معظم الحالات، ولا تحتاج إلى بادئات export. لا تضع مسافات حول الرمز =، لأن KEY = value ينشئ متغيرًا اسمه حرفيًا KEY ، مع وجود مسافة بادئة في قيمته.

يؤدي تعذّر العثور على المسار env_file إلى حدوث خطأ، ويتوقف Compose. اجعل الملف اختياريًا إذا كان غيابه ممكنًا بشكل مشروع:

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

يضبط environment المتغيرات inline

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

يُقبل بناءان نحويان: صيغة التعيين أعلاه، وصيغة القائمة التي تستخدم - GREETING=from_environment. ويتصرفان بالطريقة نفسها. تحتوي صيغة القائمة على ميزة إضافية: يمرر المفتاح المجرد الذي لا يملك قيمة المتغير من shell الذي شغّلت فيه docker compose.

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

يطبع ذلك from_my_shell. شغّله من دون تعيين GREETING في shell، وسيضبط Compose لا شيء من دون إصدار تحذير. من المهم معرفة حالات فشل التمرير الصامت، لأن الخدمة التي تبدأ بمتغير كلمة مرور فارغ غالبًا ما تبدأ بنجاح، وتصبح مكشوفة بالكامل.

أيّهما له الأولوية

يوثّق Docker ترتيب الأولوية، من الأعلى إلى الأدنى: docker compose run -e في سطر الأوامر، ثم environment أو env_file اللذان تُستبدل قيمتاهما من shell أو من ملف env، ثم environment العادي في ملف Compose، ثم env_file، ثم توجيه ENV المضمّن في الصورة.

الخلاصة للاستخدام اليومي: يتغلب 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 يتغلب على سطر الأوامر.

عندما يتصرف أحد الحاويات كما لو أن إعداداتك لم تُطبَّق، فلا تخمّن. يطبع docker compose config الملف المحلَّل بالكامل، ويطبع docker compose config --environment متغيرات الاستبدال التي يستخدمها Compose. يتبين في معظم بلاغات «تجاهل ملف env» أن القيمة محددة مرتين على مستويين مختلفين.

لماذا تتسرّب متغيرات البيئة

عيّن كلمة مرور في 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" بنص واضح. وتكشف ثلاثة مسارات أخرى القيمة نفسها. يطبع docker compose config القيمة في الطرفية، ولذلك ينتهي بها الأمر ملصقةً في منتدى دعم. ويمكن لأي عملية داخل الحاوية قراءة /proc/1/environ، كما ترث كل عملية فرعية هذا المتغير. كذلك، تفرغ معالجات تعطل التطبيقات عادةً البيئة كاملةً في سجل أو تقرير خطأ.

عضوية مجموعة docker تعادل عمليًا امتلاك root على المضيف، لذلك لا يمكنك اعتبارها حدًا للصلاحيات. يشرح الدليل حسابات المستخدمين ذات الامتيازات الأقل على خادم VPS سبب تقييد هذه المجموعة على أي خادم مشترك.

تحفظ أسرار Compose القيمة في ملف

يدعم Compose الأسرار المستندة إلى الملفات. تُثبَّت القيمة داخل الحاوية كملف بدلًا من حقنها في البيئة.

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

يُثبَّت السر في المسار /run/secrets/db_password داخل الحاوية. الاسم الذي يأتي بعد الشرطة المائلة هو اسم السر من كتلة المستوى الأعلى secrets:.

اللاحقة _FILE هي اصطلاح تستخدمه Docker Official Images، بما في ذلك postgres وmysql وmariadb. تتحقق نصوص entrypoint هذه من VARNAME_FILE، ثم تقرأ الملف وتستخدم محتوياته. هذه ليست ميزة في Docker، ولذلك لا تعمل إلا عندما تطبقها الصورة. راجع وثائق الصورة قبل افتراض أن SOMETHING_FILE سيُحترم. يمكن للتطبيقات التي لا تدعم ذلك قراءة الملف بنفسها عند بدء التشغيل، أو يمكنك تمرير المسار وترك entrypoint الخاص بك يتولى ذلك.

تحقق من داخل الحاوية قيد التشغيل:

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

يعرض الأمر الأول كلمة المرور. لا يعرض الأمر الثاني شيئًا، لأن القيمة لم تدخل البيئة مطلقًا. هذه هي الفكرة الأساسية: يعرض docker inspect في هذه الحاوية المسار غير الحساس فقط.

احمِ الملف المصدر على المضيف، لأن السر لا يحافظ على خصوصيته إلا بقدر خصوصية الملف الموجود خلفه:

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، وأودع ملف app.env.example يحتوي على أسماء المفاتيح مع قيم فارغة بدلًا من ذلك. كلمة المرور التي أودعت في المستودع هي كلمة مرور يجب تدويرها.

يعني تدوير قيمة إعادة تشغيل الخدمة. تُقرأ متغيرات البيئة مرة واحدة عند بدء عملية الحاوية، لذلك لا يغير تعديل الملف شيئًا حتى تشغّل docker compose up -d --force-recreate db. هذا هو النمط نفسه المستخدم في دليل تشغيل n8n خلف HTTPS على VPS، حيث يوجد مفتاح التشفير خارج ملف compose.

تقسيم الإعدادات حسب البيئة

يقرأ Compose الملف .env من دليل المشروع افتراضيًا. حدّد موقعًا آخر باستخدام --env-file.

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

تُقرأ الملفات المتعددة بالترتيب، وتلغي الملفات اللاحقة إعدادات الملفات السابقة. احتفظ بالقيم الافتراضية غير السرية في ملف مُدرج في نظام التحكم بالإصدارات، وبالأسرار في ملف لا يغادر الخادم مطلقًا. وينطبق الأمر نفسه على 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 مشفّرة؟

لا. يُركَّب السر المستند إلى ملف داخل الحاوية كملف نصي عادي في /run/secrets/<name>، ويبقى الملف المصدر على قرص المضيف من دون تشفير. الفائدة هنا هي تحديد النطاق، لا التشفير: تبقى القيمة خارج بيئة الحاوية، وخارج مخرجات docker inspect، وخارج تفريغات الأعطال التي تطبع البيئة.

هل يمكنني استخدام علامات الاقتباس والمسافات في ملف env؟

استخدم KEY=value with spaces واترك علامات الاقتباس خارجًا. يتعامل Compose مع بقية السطر بالكامل باعتبارها القيمة، لذلك تنتهي علامات الاقتباس عادةً كمحارف حرفية داخل القيمة. لا تضع مسافات حول =، لأن المفتاح سيحتوي عندئذٍ على مسافة زائدة في نهايته، ولن يطابقه أي شيء.