Docker Compose-এ .env, env_file ও environment-এর পার্থক্য
Docker Compose-এ .env, env_file ও environment কীভাবে আলাদা, কোনটির মান কার্যকর হয় এবং কেন password রাখা নিরাপদ নয়, বাস্তব command ও precedence উদাহরণসহ জানুন।
মানুষ যে 3টি জিনিসকে env file বলে
Docker Compose-এ একই রকম নামের 3টি পৃথক ব্যবস্থা আছে। .env file, Compose file পার্স করার আগেই, compose.yaml-এর ভেতরের ${VARIABLE} placeholder-এর মান বসায়। env_file: attribute key/value জোড়ার একটি file লোড করে container-এর environment-এ যোগ করে। environment: attribute compose file-এ সরাসরি লেখা variable container-এ সেট করে। এগুলো পরস্পরের বিকল্প নয়। দুটি ব্যবস্থায় একই key সেট হলে, কোনটির মান কার্যকর হবে তা নথিভুক্ত precedence order দ্বারা নির্ধারিত হয়।
এই guide-এ প্রতিটি ব্যবস্থা কীভাবে কাজ করে তা দেখানো হবে। চালানো যায় এমন একটি command দিয়ে precedence প্রমাণ করা হবে। এরপর আরও গুরুত্বপূর্ণ বিষয়টি ব্যাখ্যা করা হবে: যে কেউ docker inspect চালাতে পারলে environment variable পড়তে পারে। তাই password এগুলোর মধ্যে রাখা উচিত নয়। compose file সম্পর্কে আপনি নতুন হলে, VPS-এ Docker Compose-এর প্রাথমিক বিষয় দিয়ে শুরু করুন। এরপর configuration-এর জন্য এখানে ফিরে আসুন।
.env ফাইলটি compose ফাইলের জন্য, কনটেইনারের জন্য নয়
একটি ডিরেক্টরি তৈরি করুন এবং এতে দুটি ফাইল রাখুন।
mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .envservices:
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 demoprintenv ALPINE_TAG status 1 নিয়ে বন্ধ হয় এবং কিছুই প্রিন্ট করে না। কনটেইনারের ভেতরে ভেরিয়েবলটি নেই। এটিই সবচেয়ে সাধারণ ভুল বোঝাবুঝি: .env compose ফাইল কনফিগার করেছে, প্রসেসটি নয়। POSTGRES_PASSWORD=hunter2-সহ একটি .env ফাইল থাকলেও আপনার database-এর জন্য সেটি কিছুই করে না, যদি না compose ফাইলের কোনো অংশ সেটিকে রেফারেন্স করে।
ভেরিয়েবল সেট না থাকলে বা খালি থাকলে ${NAME:-default} একটি fallback মান দেয়। ${NAME:?message} Compose-কে শুরু হতে অস্বীকার করতে এবং আপনার বার্তা প্রিন্ট করতে বাধ্য করে। নিরাপদ ডিফল্ট নেই এমন মানের জন্য এটিই সঠিক পছন্দ।
env_file কনটেইনারে ভেরিয়েবল লোড করে
env_file: অ্যাট্রিবিউট এক বা একাধিক ফাইলের নাম নির্ধারণ করে। এই ফাইলগুলোর কনটেন্ট কনটেইনারের এনভায়রনমেন্ট ভেরিয়েবলে পরিণত হয়।
printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.envservices:
demo:
image: alpine:3.20
command: printenv GREETING
env_file:
- ./app.envdocker compose run --rm demoএটি from_env_file প্রিন্ট করে। ফাইলের ফরম্যাট হলো সাধারণ KEY=value লাইন, প্রতি লাইনে একটি করে। # দিয়ে মন্তব্য শুরু হয়। এটি shell নয়। বেশিরভাগ ক্ষেত্রে উদ্ধৃতি মানের অংশ হিসেবেই রাখা হয় এবং export prefix প্রয়োজন হয় না। = চিহ্নের চারপাশে space দেবেন না, কারণ KEY = value এমন একটি ভেরিয়েবল তৈরি করে যার নাম আক্ষরিকভাবে KEY এবং যার মানের শুরুতে একটি space থাকে।
env_file path না থাকলে এটি একটি ত্রুটি এবং Compose বন্ধ হয়ে যায়। ফাইলটি বৈধভাবে অনুপস্থিত থাকতে পারে হলে সেটিকে optional হিসেবে চিহ্নিত করুন:
env_file:
- path: ./app.env
required: falseenvironment inline-এ variables সেট করে
services:
demo:
image: alpine:3.20
command: printenv GREETING
environment:
GREETING: from_environmentদুটি syntax গ্রহণযোগ্য: উপরের mapping form এবং - GREETING=from_environment ব্যবহার করা list form। দুটির আচরণ একই। list form-এ একটি অতিরিক্ত সুবিধা আছে: কোনো value ছাড়া bare key দিলে, আপনি যে shell থেকে docker compose চালিয়েছেন সেখান থেকে variable-টি pass through হয়।
environment:
- GREETINGGREETING=from_my_shell docker compose run --rm demoএটি from_my_shell প্রিন্ট করে। shell-এ GREETING সেট না করে এটি চালালে Compose কোনো value সেট করে না এবং কোনো warning দেখায় না। নীরবে pass-through ব্যর্থ হওয়ার বিষয়টি জানা গুরুত্বপূর্ণ, কারণ empty password variable নিয়ে কোনো service প্রায়ই সফলভাবে চালু হয় এবং কার্যত সবার জন্য উন্মুক্ত থাকে।
কোনটি কার্যকর হয়
Docker অগ্রাধিকারের ক্রমটি নথিবদ্ধ করেছে, সর্বোচ্চ থেকে শুরু করে: কমান্ড লাইনে থাকা docker compose run -e, এরপর environment বা env_file, যেগুলোর মান আপনার shell অথবা env file থেকে নেওয়া হয়, এরপর compose file-এ থাকা সাধারণ environment, তারপর env_file, এবং সবশেষে image-এ সংরক্ষিত ENV directive।
দৈনন্দিন কাজের সংক্ষিপ্ত নিয়ম: environment:, env_file:-কে অগ্রাহ্য করে, আর কমান্ড লাইনে থাকা -e উভয়ের ওপর প্রাধান্য পায়। এটি একটি file-এ পরীক্ষা করুন।
services:
demo:
image: alpine:3.20
command: printenv GREETING
env_file:
- ./app.env
environment:
GREETING: from_environmentdocker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETINGপ্রথম কমান্ডটি from_environment প্রদর্শন করে। এর কারণ, environment:, app.env-এ থাকা মানকে অগ্রাহ্য করেছে। দ্বিতীয় কমান্ডটি from_cli প্রদর্শন করে। Compose file-এর কোনো কিছুই কমান্ড লাইনের মানকে অগ্রাহ্য করে না।
কোনো container এমন আচরণ করলে যেন আপনার configuration প্রয়োগ হয়নি, অনুমান করবেন না। docker compose config সম্পূর্ণ resolved file প্রদর্শন করে, আর docker compose config --environment দেখায় Compose কোন interpolation variables ব্যবহার করছে। “আমার env file উপেক্ষা করা হচ্ছে” ধরনের অধিকাংশ সমস্যার কারণ হলো একই মান দুটি ভিন্ন স্তরে নির্ধারিত থাকা।
পরিবেশ ভেরিয়েবল কেন ফাঁস হয়
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 পড়তে পারে এবং প্রতিটি child process এই ভেরিয়েবল উত্তরাধিকারসূত্রে পায়। এ ছাড়া, অ্যাপ্লিকেশনের ক্র্যাশ হ্যান্ডলারগুলো নিয়মিত পুরো environment কোনো log বা error report-এ dump করে।
docker গ্রুপের সদস্যপদ কার্যত host-এ root-সমতুল্য, তাই এটিকে নির্ভরযোগ্য privilege boundary হিসেবে ব্যবহার করা যায় না। VPS-এ least privilege ব্যবহারকারী অ্যাকাউন্ট-এর গাইডে ব্যাখ্যা করা হয়েছে, 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.txtsecret-টি container-এর ভিতরে /run/secrets/db_password-এ mount করা হয়। slash-এর পরের নামটি শীর্ষ-স্তরের secrets: block থেকে নেওয়া secret-এর নাম।
_FILE suffix-টি postgres, mysql এবং mariadb-সহ Docker Official Images-এ ব্যবহৃত একটি প্রচলিত রীতি। ওই entrypoint script-গুলো VARNAME_FILE পরীক্ষা করে, ফাইলটি পড়ে এবং তার বিষয়বস্তু ব্যবহার করে। এটি Docker-এর কোনো feature নয়। তাই image-টি এটি বাস্তবায়ন করলেই কেবল কাজ করবে। SOMETHING_FILE কার্যকর হবে ধরে নেওয়ার আগে image-এর documentation পরীক্ষা করুন। এটি সমর্থন করে না এমন application-গুলো সাধারণত startup-এর সময় নিজেরাই ফাইলটি পড়তে পারে। অন্যথায় path-টি pass করে নিজের 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-টির গোপনীয়তা ওই file-টির সুরক্ষার ওপরই নির্ভর করে:
chmod 600 db_password.txtVPS-এ ব্যবহারিক মধ্যপন্থা
অনেক self hosted image _FILE variable সমর্থন করে না, তাই environment variable-ই একমাত্র উপায়। একটি একক administrator VPS-এ বাস্তবসম্মত লক্ষ্য হলো আপনার project directory-র এমন file-এ value পড়ে থাকা বন্ধ করা, যা সবাই পড়তে পারে, এবং সেগুলোকে 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.envinstall -m 600 শুরুতেই নির্ধারিত mode-সহ file তৈরি করে। তাই এমন কোনো সময় থাকে না, যখন file-টি সবাই পড়তে পারে। এর মালিক root। ফলে server-এর non root user এটি পড়তে পারে না। তবে যে কেউ docker চালাতে পারে, সে container থেকে value পড়তে পারে। *.env এবং .env .gitignore-এ যোগ করুন। এরপর key name-গুলো empty value-সহ রাখা একটি app.env.example commit করুন। Commit করা password পরিবর্তন করতে হবে।
কোনো value পরিবর্তন করলে service restart করতে হয়। Container process শুরু হওয়ার সময় environment variable একবার পড়া হয়। তাই 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একাধিক ফাইল ক্রমানুসারে পড়া হয়, এবং পরের ফাইলগুলো আগের ফাইলের মান প্রতিস্থাপন করে। গোপন নয় এমন ডিফল্ট মানগুলো কমিট করা একটি ফাইলে রাখুন এবং গোপন মানগুলো এমন একটি ফাইলে রাখুন, যা কখনো সার্ভারের বাইরে যাবে না। env_file:-এর ক্ষেত্রেও একই নিয়ম প্রযোজ্য; একই key একাধিকবার থাকলে তালিকার সর্বশেষ ফাইলের মান কার্যকর হয়।
FAQ
আমার .env ফাইল container-এর ভিতরে উপেক্ষিত হচ্ছে কেন?
এটি উপেক্ষিত হচ্ছে না। .env ফাইলটি কেবল compose ফাইলে থাকা ${NAME} placeholder প্রতিস্থাপন করে। এটি কখনো container-এর ভিতরে variable সেট করে না। মানটি container-এর ভিতরে পাঠাতে এটি reference করুন: 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-এর মান নীরবে ব্যবহার করা হয় না।
Compose যে চূড়ান্ত মান ব্যবহার করবে, তা কীভাবে দেখব?
সমস্ত interpolation প্রয়োগসহ সম্পূর্ণ resolved compose ফাইল দেখাতে docker compose config চালান। ইতিমধ্যে চলমান container-এর ক্ষেত্রে, তার process যে মান পেয়েছে তা ঠিকভাবে দেখায় docker inspect <container> --format '{{json .Config.Env}}'।
Compose secrets কি encrypted?
না। file based secret একটি plain file হিসেবে /run/secrets/<name>-এ container-এর ভিতরে mount করা হয়, এবং source file-টি host disk-এ unencrypted অবস্থায় থাকে। এর সুবিধা encryption নয়, scope: মানটি container environment-এর বাইরে, docker inspect output-এর বাইরে এবং environment প্রদর্শনকারী crash dump-এর বাইরেও থাকে।
env ফাইলে quotes এবং spaces কি ব্যবহার করা যায়?
KEY=value with spaces ব্যবহার করুন এবং quotes বাদ দিন। Compose পুরো অবশিষ্ট line-কে মান হিসেবে বিবেচনা করে। তাই quotes সাধারণত মানের মধ্যে literal character হিসেবে থেকে যায়। =-এর চারপাশে কখনো spaces দেবেন না, কারণ তখন key-এর শেষে একটি trailing space যুক্ত হয় এবং কোনো কিছুর সঙ্গে এটি মেলে না।