Docker Compose: Pagkakaiba ng .env, env_file at secrets
Alamin ang pagkakaiba ng .env, env_file at environment sa Docker Compose, kasama ang precedence, tamang gamit, at kung saan dapat ilagay ang mga password.
Tatlong bagay na tinatawag ng mga tao na env file
May tatlong magkahiwalay na mekanismo ang Docker Compose na halos magkapareho ang pangalan. Ang .env file ay naglalagay ng mga value sa mga ${VARIABLE} placeholder sa loob mismo ng compose.yaml, bago pa man i-parse ng Compose ang file. Ang env_file: attribute ay naglo-load ng file ng mga key/value pair sa environment ng container. Ang environment: attribute ay direktang nagse-set ng mga variable sa container, na isinusulat sa compose file. Hindi maaaring pagpalitin ang mga ito. Kapag dalawa sa mga ito ang nag-set ng parehong key, nakatakda ang mananalo ayon sa dokumentadong pagkakasunud-sunod ng precedence.
Ipinapakita ng gabay na ito kung paano gumagana ang bawat isa. Pinatutunayan nito ang precedence gamit ang command na maaari mong patakbuhin. Tatalakayin din nito ang mas mahalagang punto: mababasa ng sinumang makapagpatakbo ng docker inspect ang mga environment variable, kaya hindi dapat ilagay ang mga password sa mga ito. Kung bago ka sa mga compose file sa pangkalahatan, magsimula sa Mga pangunahing kaalaman sa Docker Compose sa isang VPS at bumalik dito para sa configuration.
Ang .env file ay para sa compose file, hindi sa container
Gumawa ng directory at maglagay ng dalawang file dito.
mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .envservices:
demo:
image: alpine:${ALPINE_TAG}
command: printenv ALPINE_TAGNgayon, itanong sa Compose kung ano talaga ang na-parse nito.
docker compose configIpinapakita ng output ang image: alpine:3.20. Wala na ang placeholder dahil naganap ang interpolation sa oras ng pag-parse. Hinahanap ng Compose ang .env sa project directory, na siyang directory na naglalaman ng compose file, at pinapalitan nito ang bawat ${NAME} na makita nito.
Pagkatapos, patakbuhin ang service.
docker compose run --rm demoLumalabas ang printenv ALPINE_TAG na may status 1 at walang ini-print. Hindi umiiral ang variable sa loob ng container. Ito ang pinakakaraniwang maling pagkaunawa: kino-configure ng .env ang compose file, hindi ang process. Ang isang .env file na may POSTGRES_PASSWORD=hunter2 dito ay walang ginagawa para sa database mo maliban kung may bahagi ng compose file na tumutukoy rito.
Nagbibigay ang ${NAME:-default} ng fallback kapag unset o walang laman ang variable. Pinapahinto ng ${NAME:?message} ang Compose na magsimula at ipinapakita ang mensahe mo. Ito ang tamang piliin para sa value na walang ligtas na default.
Naglo-load ang env_file ng mga variable sa container
Tinutukoy ng attribute na env_file: ang isa o higit pang file na ang laman ay nagiging mga environment variable ng container.
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 demoIpinapakita nito ang from_env_file. Ang format ng file ay mga plain KEY=value line, tig-isa bawat line, at ang # sa simula ay nagmamarka ng comment. Hindi ito shell. Sa karamihan ng mga kaso, nananatiling bahagi ng value ang mga quote, at hindi kailangan ang mga prefix na export. Huwag maglagay ng mga space sa paligid ng sign na =, dahil ang KEY = value ay lumilikha ng variable na literal na may pangalang KEY at may leading space ang value nito.
Error ang nawawalang path na env_file, at hihinto ang Compose. Markahan ito bilang optional kung maaaring lehitimong wala ang file:
env_file:
- path: ./app.env
required: falseenvironment inline na nagse-set ng mga variable
services:
demo:
image: alpine:3.20
command: printenv GREETING
environment:
GREETING: from_environmentTinatanggap ang dalawang syntax: ang mapping form sa itaas at ang list form na gumagamit ng - GREETING=from_environment. Magkapareho ang kanilang paggana. May isang karagdagang gamit ang list form: ipinapasa ng bare key na walang value ang variable mula sa shell kung saan mo pinatakbo ang docker compose.
environment:
- GREETINGGREETING=from_my_shell docker compose run --rm demoIni-print nito ang from_my_shell. Patakbuhin ito nang hindi sine-set ang GREETING sa shell, at walang ise-set ang Compose; wala rin itong warning. Mahalagang malaman ang mga silent pass-through failure dahil ang service na nagsisimula gamit ang walang laman na password variable ay kadalasang matagumpay na nagsisimula at ganap na walang proteksyon.
Alin ang mananaig
Idinodokumento ng Docker ang pagkakasunod-sunod ng precedence, mula sa pinakamataas: docker compose run -e sa command line, kasunod ang environment o env_file na ang value ay ini-interpolate mula sa iyong shell o sa isang env file, pagkatapos ang plain na environment sa compose file, kasunod ang env_file, at panghuli ang ENV directive na naka-bake sa image.
Ang maikling bersyon para sa pang-araw-araw na paggamit: nananaig ang environment: laban sa env_file:, at nilalampasan ng -e sa command line ang pareho. Patunayan ito sa isang 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 GREETINGAng unang command ay nagpi-print ng from_environment, kaya na-override ng environment: ang value sa app.env. Ang ikalawa ay nagpi-print ng from_cli. Walang nasa compose file ang nag-o-override sa command line.
Kapag kumikilos ang isang container na para bang hindi nailapat ang iyong config, huwag manghula. Ini-print ng docker compose config ang ganap na na-resolve na file, at ini-print ng docker compose config --environment ang interpolation variables na ginagamit ng Compose. Kadalasang ang sanhi ng mga ulat na “hindi pinansin ang env file ko” ay isang value na naitakda nang dalawang beses sa magkaibang level.
Bakit nagkakalat ang environment variables
Magtakda ng password sa environment: at maiimbak ito sa configuration ng container sa disk. Makikita ito ng sinumang user sa docker group.
docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'Makikita sa output ang "DB_PASSWORD=hunter2" bilang plain text. Tatlo pang path ang naglalantad ng parehong value. Ipi-print ito ng docker compose config sa terminal, kaya nakokopya ito sa isang support forum. Mababasa ng anumang process sa loob ng container ang /proc/1/environ, at mamanahin ng bawat child process ang variable. Karaniwan ding itinatala ng mga application crash handler ang buong environment sa isang log o error report.
Ang pagiging miyembro ng docker group ay katumbas na halos ng pagkakaroon ng root access sa host. Kaya hindi ito privilege boundary na maaasahan mo. Ipinapaliwanag ng gabay na mga user account na may least privilege sa isang VPS kung bakit dapat limitahan ang access sa group na iyon sa anumang shared box.
Ang Compose secrets ay nagpapanatili ng value sa isang file
Sinusuportahan ng Compose ang mga file-based secret. Ima-mount ang value sa container bilang file sa halip na i-inject sa environment.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txtIma-mount ang secret sa /run/secrets/db_password sa loob ng container. Ang pangalan pagkatapos ng slash ay ang secret name mula sa top-level na secrets: block.
Ang _FILE suffix ay isang convention na ginagamit ng Docker Official Images, kabilang ang postgres, mysql at mariadb. Sinusuri ng mga entrypoint script na iyon kung may VARNAME_FILE, binabasa ang file, at ginagamit ang laman nito. Hindi ito Docker feature, kaya gagana lamang ito kung ipinapatupad ito ng image. Suriin ang documentation ng image bago ipagpalagay na kikilalanin ang SOMETHING_FILE. Ang mga application na hindi sumusuporta rito ay kadalasang maaaring bumasa mismo sa file sa startup. Maaari mo ring ipasa ang path at hayaan itong basahin ng sarili mong entrypoint.
I-verify mula sa loob ng tumatakbong container:
docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORDIpinapakita ng una ang password. Walang ipinapakita ang pangalawa dahil hindi kailanman napunta ang value sa environment. Iyan ang buong layunin: ipinapakita ng docker inspect sa container na ito ang harmless na path lamang.
Protektahan ang source file sa host dahil kasing-private lamang ng file na pinagmumulan nito ang secret:
chmod 600 db_password.txtPraktikal na gitnang solusyon sa isang VPS
Maraming self hosted image ang hindi sumusuporta sa mga variable na _FILE, kaya environment variables ang tanging paraan. Sa iisang VPS na pinamamahalaan ng isang administrator, ang praktikal na layunin ay alisin ang mga value sa file na nababasa ng lahat sa project directory at panatilihin ang mga ito sa labas ng 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.envGinagawa ng install -m 600 ang file na nakatakda na ang mode, kaya walang panahong nababasa ito ng lahat. Pagmamay-ari ito ng root, kaya hindi ito mababasa ng non root user sa server. Gayunman, mababasa pa rin ng sinumang maaaring magpatakbo ng docker ang value mula sa container. Idagdag ang *.env at .env sa .gitignore, at mag-commit ng app.env.example na naglalaman ng mga key name na walang laman ang mga value. Kapag na-commit ang password, kailangan itong i-rotate.
Ang pag-rotate ng value ay nangangahulugang pag-restart ng service. Isang beses lang binabasa ang environment variables kapag nagsisimula ang container process, kaya walang epekto ang pag-edit sa file hanggang sa patakbuhin mo ang docker compose up -d --force-recreate db. Ito rin ang pattern na ginagamit sa gabay na n8n sa likod ng HTTPS sa isang VPS, kung saan nasa labas ng compose file ang encryption key.
Paghahati ng configuration ayon sa environment
Binabasa ng Compose ang .env mula sa project directory bilang default. Ituro ito sa ibang lokasyon gamit ang --env-file.
docker compose --env-file .env.staging configBinabasa ang maraming file ayon sa pagkakasunod-sunod, at ino-override ng mga susunod na file ang mga nauna. Ilagay ang mga default na hindi lihim sa isang naka-commit na file, at ilagay ang mga secret sa file na hindi kailanman iniiwan sa server. Nalalapat din ito sa env_file:, kung saan mananaig ang huling file sa listahan kapag may duplicate na key.
FAQ
Bakit hindi pinapansin ang aking .env file sa loob ng container?
Hindi ito binabalewala. Ang .env file ay nagsasagawa lamang ng substitution sa mga ${NAME} placeholder sa compose file. Hindi ito kailanman nagse-set ng mga variable sa loob ng container. Para maipasa ang value sa container, i-reference ito: environment: { KEY: "${NAME}" }, o gamitin ang env_file: ./that-file.env sa halip.
Alin ang mananaig: environment o env_file?
Nananaig ang environment:. Ayon sa dokumentadong order ng Docker, mas mataas ang environment attribute kaysa sa env_file attribute, at parehong mas mababa ang mga ito kaysa sa docker compose run -e sa command line. Kung nakatakda ang isang key sa parehong lugar, tahimik na hindi ginagamit ang value sa env_file.
Paano ko makikita ang final na value na gagamitin ng Compose?
Patakbuhin ang docker compose config upang i-print ang ganap na na-resolve na compose file na nailapat na ang lahat ng interpolation. Para sa container na tumatakbo na, ipinapakita ng docker inspect <container> --format '{{json .Config.Env}}' kung ano mismo ang natanggap ng proseso nito.
Naka-encrypt ba ang Compose secrets?
Hindi. Ang file based secret ay mina-mount sa container bilang plain file sa /run/secrets/<name>, at ang source file ay nananatiling hindi naka-encrypt sa host disk. Ang pakinabang nito ay scope, hindi encryption: nananatili ang value sa labas ng container environment, sa labas ng output ng docker inspect, at sa labas ng crash dumps na nagpi-print ng environment.
Maaari ba akong gumamit ng quotes at spaces sa env file?
Gamitin ang KEY=value with spaces at huwag ilagay ang quotes. Itinuturing ng Compose na value ang buong natitirang bahagi ng linya, kaya karaniwang nagiging literal na mga character sa value ang quotes. Huwag kailanman maglagay ng spaces sa paligid ng =, dahil magkakaroon ng trailing space ang key at walang makakatugma rito.