SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-07

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

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

الأشياء الثلاثة التي يسميها الناس ملف env

يحتوي Docker Compose على 3 آليات منفصلة ذات أسماء متشابهة بشكل مربك. يملأ ملف .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

تعيين المتغيرات في البيئة بشكل مضمن

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

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

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

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

ما الذي تكون له الأولوية

يوثّق Docker ترتيب الأولوية، من الأعلى إلى الأدنى: docker compose run -e في سطر الأوامر، ثم environment أو env_file التي تُستبدل قيمتها من الصدفة أو من ملف بيئة، ثم 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. يتضح في معظم بلاغات «ملف البيئة الخاص بي مُهمل» أن القيمة محددة مرتين على مستويين مختلفين.

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

عيّن كلمة مرور في 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، لذا لا يستطيع مستخدم عادي على الخادم قراءته، لكن أي شخص يستطيع تشغيل 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 مع بقية السطر بالكامل باعتبارها القيمة، لذلك تنتهي علامات الاقتباس عادةً كمحارف حرفية ضمن القيمة. لا تضع مسافات حول = مطلقاً، لأن المفتاح سيحمل عندها مسافة زائدة في نهايته، ولن يطابقه أي شيء.