Docker Compose मधील .env, env_file आणि secrets
Docker Compose मधील .env, env_file आणि environment यांतील फरक जाणून घ्या. कोणती value जिंकते, precedence कसे ठरते आणि passwords secrets मध्ये का ठेवावेत हे समजून घ्या.
env file म्हणून ओळखल्या जाणाऱ्या तीन गोष्टी
Docker Compose मध्ये नाव साधर्म्यामुळे गोंधळ निर्माण करणाऱ्या तीन स्वतंत्र यंत्रणा आहेत. Compose YAML वाचल्यानंतर आणि कोणत्याही गोष्टीचे प्रमाणीकरण किंवा execution करण्यापूर्वी .env फाइल स्वतः compose.yaml मधील ${VARIABLE} placeholders ची मूल्ये भरते. env_file: attribute key/value जोड्यांची फाइल वाचून ती container च्या environment मध्ये लोड करते. environment: attribute variables थेट container वर सेट करते आणि त्यांची नोंद compose file मध्ये केलेली असते. या यंत्रणा एकमेकांच्या पर्याय नाहीत. त्यांपैकी दोन यंत्रणांनी समान key सेट केल्यास, कोणती मूल्ये लागू होतील हे documented precedence order नुसार निश्चित होते.
या मार्गदर्शकात प्रत्येक यंत्रणा कार्यरत दाखवली आहे. तुम्ही चालवू शकता अशा command द्वारे precedence सिद्ध केले आहे. त्यानंतर अधिक महत्त्वाचा मुद्दा स्पष्ट केला आहे: docker inspect चालवू शकणाऱ्या कोणालाही environment variables वाचता येतात. त्यामुळे passwords त्यात ठेवू नयेत. compose files ची मूलभूत माहिती हवी असल्यास VPS वरील Docker Compose ची मूलभूत माहिती पासून सुरुवात करा आणि configuration साठी येथे परत या.
.env फाइल ही compose फाइलसाठी असते, कंटेनरसाठी नाही
एक directory तयार करा आणि त्यात दोन फाइल ठेवा.
mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .envservices:
demo:
image: alpine:${ALPINE_TAG}
command: printenv ALPINE_TAGआता Compose ने प्रत्यक्षात काय parse केले ते पाहा.
docker compose configआउटपुटमध्ये image: alpine:3.20 दिसते. Placeholder नाहीसा झाला आहे, कारण Compose ने फाइल लोड करताना interpolation केले. Compose project directory मध्ये .env शोधते: तुम्ही ते सेट केले असल्यास --project-directory ने दिलेली directory, अन्यथा -f सोबत दिलेल्या पहिल्या फाइलची directory, आणि तेही नसल्यास सध्याची directory. YAML मधील values मध्ये आढळणारे ${NAME} placeholders ते बदलते. Keys चे interpolation कधीही केले जात नाही. तसेच दुप्पट $$ substitution ऐवजी literal dollar sign तयार करते.
आता service चालवा.
docker compose run --rm demoprintenv ALPINE_TAG status 1 सह बंद होते आणि काहीही print करत नाही. हे variable container मध्ये उपलब्ध नसते. हा सर्वात सामान्य गैरसमज आहे: .env ने compose फाइल configure केली, process नाही. POSTGRES_PASSWORD=hunter2 असलेली .env फाइल ठेवली, तरी compose फाइलमधील कोणताही भाग तिचा संदर्भ घेत नसेल तर database साठी त्याचा काहीही परिणाम होत नाही.
Variable unset किंवा empty असल्यास ${NAME:-default} fallback value देते. ${NAME:?message} मुळे Compose सुरू होण्यास नकार देते आणि तुमचा message print करते. सुरक्षित default नसलेल्या value साठी हा योग्य पर्याय आहे.
env_file कंटेनरमध्ये variables लोड करते
`env_file:` attribute अशा एक किंवा अधिक files ची नावे निर्दिष्ट करते, ज्यांमधील contents container environment variables बनतात.
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 छापते. File format साध्या KEY=value lines चा असतो; प्रत्येक line स्वतंत्र असते आणि # पासून comment सुरू होतो. हे shell नाही, मात्र Compose काही shell-सदृश नियमांनुसार त्याचे parsing करते. आजूबाजूचे quotes काढले जातात, त्यामुळे KEY="value" आणि KEY=value यांचा result समान असतो. Double-quoted आणि unquoted values मध्ये ${...} placeholders interpolate केले जातात, तर single-quoted values जशाच्या तशा वापरल्या जातात. export prefix स्वीकारला जातो, परंतु तो आवश्यक नाही. = sign च्या आजूबाजूचे spaces काढले जातात. Inline comment बाबत काळजी घ्या: unquoted value मध्ये space नंतर # आल्यास comment सुरू होतो. त्यामुळे #` असलेली कोणतीही value quote करा.
`env_file` path सापडला नाही, तर ती error असते आणि Compose थांबते. File वैधरीत्या अनुपलब्ध असू शकत असल्यास ती 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. दोन्हींचे वर्तन समान असते. दोन्हीमध्ये आणखी एक सुविधा आहे: value नसलेली key तुम्ही docker compose चालवलेल्या shell मधून variable pass through करते. List form मध्ये ती फक्त - GREETING असते, तर mapping form मध्ये colon नंतर काहीही नसलेले GREETING: असते.
environment:
- GREETINGGREETING=from_my_shell docker compose run --rm demoयामुळे from_my_shell print होते. Shell मध्ये GREETING सेट न करता ते चालवले, तर Compose काहीही सेट करत नाही आणि कोणतीही warning दाखवत नाही. अशा silent pass-through failures ची माहिती असणे महत्त्वाचे आहे. कारण रिकाम्या password variable सह सुरू होणारी service अनेकदा यशस्वीपणे सुरू होते आणि प्रत्यक्षात पूर्णपणे असुरक्षित राहते.
कोणते प्राधान्य मिळवते
Docker प्राधान्यक्रम दस्तऐवजीकरणात सर्वाधिक प्राधान्यापासून दिला आहे: कमांड लाइनवरील docker compose run -e, त्यानंतर shell किंवा env file मधून मूल्याचे interpolation होणारे environment किंवा 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पहिल्या command मधून from_environment प्रदर्शित होते. याचा अर्थ environment: ने app.env मधील मूल्यावर अधिलिखित केले. दुसऱ्या command मधून from_cli प्रदर्शित होते. compose file मधील कोणतीही गोष्ट कमांड लाइनवरील मूल्यावर अधिलिखित करत नाही.
तुमच्या config चे परिणाम लागू झालेले नाहीत अशा प्रकारे container वागत असल्यास अंदाज लावू नका. docker compose config पूर्णपणे निराकरण केलेली file प्रदर्शित करते आणि docker compose config --environment Compose ज्या interpolation variables वर काम करत आहे ते प्रदर्शित करते. “माझी env file दुर्लक्षित केली जाते” अशा बहुतेक तक्रारींमध्ये मूल्य दोन वेगवेगळ्या स्तरांवर दोनदा सेट केलेले असते.
पर्यावरण चल लीक का धोका
environment: मध्ये password सेट केल्यास ते container च्या configuration मध्ये disk वर साठवले जाते. docker group मधील कोणताही user ते पाहू शकतो.
docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'Output मध्ये "DB_PASSWORD=hunter2" plain text स्वरूपात असते. आणखी तीन paths मधूनही हेच value उघड होते. docker compose config ते terminal वर print करते. त्यामुळे ते support forum मध्ये paste केले जाते. Parent process replacement environment देऊन child process सुरू करत नसेल, तर child processes हे variable default स्वरूपात inherit करतात. PID 1 प्रमाणेच user म्हणून चालणारी process किंवा CAP_SYS_PTRACE धारण करणारी process ते /proc/1/environ मधून वाचू शकते. सर्व प्रक्रिया root म्हणून चालणाऱ्या container मध्ये हे प्रत्येक process ला शक्य असते. PID 1 root म्हणून चालू असताना unprivileged user म्हणून चालणाऱ्या application ला त्या file वर permission error मिळतो. तरीही त्या application च्या स्वतःच्या environment मध्ये हे variable असते. तसेच application चे crash handlers अनेकदा संपूर्ण environment log किंवा error report मध्ये dump करतात.
docker group चे membership host वर प्रत्यक्षात root इतकेच सामर्थ्य देते. त्यामुळे याला privilege boundary म्हणून गृहीत धरू नका. VPS वरील किमान privilege असलेली user accounts यावरील मार्गदर्शक shared box वर या group चे membership मर्यादित ठेवणे का आवश्यक आहे हे स्पष्ट करते.
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 नंतरचे नाव हे top-level secrets: block मधील secret चे नाव आहे.
_FILE suffix ही Docker Official Images मध्ये वापरली जाणारी पद्धत आहे. यामध्ये postgres, mysql आणि mariadb यांचा समावेश होतो. त्यांच्या entrypoint scripts VARNAME_FILE तपासतात, फाइल वाचतात आणि तिच्या मजकुराचा वापर करतात. हे Docker चे feature नाही. त्यामुळे image मध्ये त्याची अंमलबजावणी असल्यासच ते कार्य करते. 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.txtVPS वरील व्यावहारिक मध्यम मार्ग
अनेक self hosted images _FILE variables ला समर्थन देत नाहीत. त्यामुळे environment variables वापरणे हाच एकमेव मार्ग असतो. एकाच administrator कडून व्यवस्थापित होणाऱ्या VPS वर वास्तववादी उद्दिष्ट असे आहे की तुमच्या project directory मधील सर्व वापरकर्त्यांना वाचता येणाऱ्या file मध्ये values साठून राहू नयेत आणि त्या 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 file तयार करतानाच तिचा mode सेट करते. त्यामुळे ती काही काळ सर्वांना वाचता येईल अशी राहत नाही. तिची मालकी root कडे असते. त्यामुळे त्या मशीनवरील non root user ती वाचू शकत नाही. मात्र docker चालवू शकणारी व्यक्ती container मधून value वाचू शकते. *.env आणि .env .gitignore मध्ये जोडा आणि त्याऐवजी key names व रिकाम्या values असलेली app.env.example commit करा. Commit केलेला password बदलणे आवश्यक असते.
Value बदलण्यासाठी service पुन्हा सुरू करावी लागते. Container process सुरू होताना environment variables एकदाच वाचले जातात. त्यामुळे file संपादित केल्यावर docker compose up -d --force-recreate db चालवेपर्यंत कोणताही बदल लागू होत नाही. VPS वर HTTPS मागे n8n या मार्गदर्शकातही हीच पद्धत वापरली आहे. तेथे encryption key compose file च्या बाहेर ठेवली आहे.
प्रत्येक वातावरणासाठी कॉन्फिगरेशन विभाजित करणे
Compose प्रकल्प निर्देशिकेतून डीफॉल्टनुसार .env वाचते. --env-file वापरून ते दुसऱ्या ठिकाणी निर्देशित करा.
docker compose --env-file .env.staging configएकापेक्षा अधिक फाइल्स क्रमाने वाचल्या जातात आणि नंतरच्या फाइल्स आधीच्या फाइल्समधील मूल्ये अधिलिखित करतात. गुप्त नसलेली डीफॉल्ट मूल्ये commit केलेल्या फाइलमध्ये ठेवा आणि secrets अशा फाइलमध्ये ठेवा जी कधीही serverच्या बाहेर जाणार नाही. हेच env_file: साठीही लागू होते; एकाच keyची पुनरावृत्ती असल्यास यादीतील शेवटची फाइल लागू होते.
FAQ
माझ्या container मध्ये .env फाइलकडे दुर्लक्ष का केले जाते?
तिच्याकडे दुर्लक्ष केले जात नाही. .env फाइल compose फाइलमधील ${NAME} placeholders चे substitution करते. ती COMPOSE_PROJECT_NAME किंवा COMPOSE_FILE सारखे Compose चे स्वतःचे variables देखील सेट करू शकते. ती container मधील variables कधीही सेट करत नाही. मूल्य 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 मधील मूल्य शांतपणे वापरले जात नाही.
Compose वापरणार असलेले अंतिम मूल्य कसे पाहू?
सर्व interpolation लागू केलेली पूर्णपणे resolved compose फाइल छापण्यासाठी docker compose config चालवा. आधीच चालू असलेल्या container साठी, त्याच्या process ला प्रत्यक्षात कोणती मूल्ये मिळाली हे docker inspect <container> --format '{{json .Config.Env}}' ने दाखवले जाते.
Compose secrets encrypted असतात का?
नाही. File-based secret /run/secrets/<name> येथे container मध्ये plain file म्हणून mount केले जाते. Source file host disk वर unencrypted स्थितीत राहते. याचा लाभ encryption नसून scope आहे: हे मूल्य container environment मध्ये, docker inspect output मध्ये किंवा environment छापणाऱ्या crash dumps मध्ये दिसत नाही.
env फाइलमध्ये quotes आणि spaces वापरता येतात का?
होय. Compose भोवतीच्या quotes काढून टाकते. त्यामुळे KEY="value with spaces" आणि KEY=value with spaces दोन्हीमधून value with spaces मिळते. मूल्यामध्ये interpolation न करायचा $ असल्यास single quotes वापरा. # असलेले कोणतेही मूल्य quotes मध्ये लिहा, कारण unquoted असल्यास त्यापासून comment सुरू होते. = भोवतीचे spaces काढले जातात. त्यामुळे KEY = value मुळे KEY तरीही सेट होते.