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.
Ang tatlong tinatawag na env file
May tatlong magkahiwalay na mechanism ang Docker Compose na halos magkapareho ang pangalan at madaling malito. Pinupunan ng .env file ang mga ${VARIABLE} placeholder sa loob mismo ng compose.yaml, pagkatapos basahin ng Compose ang YAML at bago nito i-validate o patakbuhin ang anuman. Nilo-load ng env_file: attribute ang file ng mga key/value pair sa environment ng container. Direktang nagse-set ang environment: attribute ng mga variable sa container, na isinusulat sa compose file. Hindi maaaring ipagpalit ang mga ito. Kapag pareho silang nagse-set ng iisang key, ang mananalo ay nakatakda sa documented precedence order.
Ipinapakita ng guide na ito kung paano gumagana ang bawat isa. Pinatutunayan nito ang precedence gamit ang command na maaari mong patakbuhin. Tinalakay rin nito ang mas mahalagang bahagi: nababasa ng sinumang maaaring magpatakbo ng docker inspect ang environment variables, kaya hindi dapat ilagay roon ang mga password. Kung bago ka sa mga compose file, 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 para sa container
Gumawa ng isang directory at ilagay rito ang dalawang file.
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 habang nilo-load ng Compose ang file. Hinahanap ng Compose ang .env sa project directory: ang directory na ibinigay gamit ang --project-directory kung itinakda mo ito; kung hindi, ang directory ng unang file na ipinasa gamit ang -f; at kung wala rin, ang kasalukuyang directory. Pinapalitan nito ang mga ${NAME} placeholder na makita nito sa mga YAML value. Hindi kailanman ini-interpolate ang mga key, at ang dobleng $$ ay gumagawa ng literal na dollar sign sa halip na substitution.
Pagkatapos, patakbuhin ang service.
docker compose run --rm demoLumalabas ang printenv ALPINE_TAG na may status 1 at walang pini-print. Hindi umiiral ang variable sa loob ng container. Ito ang pinakakaraniwang maling pagkaunawa: ang .env ay nag-configure sa compose file, hindi sa process. Ang isang .env file na may POSTGRES_PASSWORD=hunter2 dito ay walang anumang epekto sa database mo maliban kung may bahagi ng compose file na tumutukoy rito.
Nagbibigay ang ${NAME:-default} ng fallback kapag unset o empty ang variable. Pinapabayaan naman ng ${NAME:?message} na tumangging mag-start ang Compose at i-print 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 env_file: attribute ang isa o higit pang file na ang laman ay magiging 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. Plain KEY=value lines ang format ng file, tig-iisa bawat linya, at ang # sa simula ay nagmamarka ng comment. Hindi ito shell file, kahit may ilang shell-like rules na bina-parse ang Compose. Inaalis ang nakapaligid na quotation mark, kaya pareho ang resulta ng KEY="value" at KEY=value. Ang ${...} placeholders ay ini-interpolate sa double-quoted at unquoted values, samantalang literal na ginagamit ang single-quoted values. Tinatanggap ang export prefix pero hindi ito kailangan. Tinatanggal ang mga space sa paligid ng = sign. Ang dapat bantayan ay ang inline comment: sa unquoted value, nagsisimula ang comment kapag may space na sinusundan ng #, kaya i-quote ang anumang value na naglalaman ng #.
Error ang nawawalang env_file path at hihinto ang Compose. Markahan itong optional kung maaaring lehitimong wala ang file:
env_file:
- path: ./app.env
required: falseenvironment ay nagse-set ng mga variable inline
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 behavior ng mga ito. May isang karagdagang feature ang dalawang form: kapag walang value ang isang key, ipinapasa nito ang variable mula sa shell kung saan mo pinatakbo ang docker compose. Sa list form, isa itong bare - GREETING, at sa mapping form, GREETING: ito na walang anumang kasunod ng colon.
environment:
- GREETINGGREETING=from_my_shell docker compose run --rm demoIpinapakita nito ang from_my_shell. Patakbuhin ito nang hindi sine-set ang GREETING sa shell, at walang ise-set ang Compose at walang ipapakitang warning. Mahalagang malaman ang mga tahimik na failure sa pass-through, dahil ang isang service na nagsisimula gamit ang walang laman na password variable ay madalas na matagumpay na nagsisimula ngunit ganap na bukas sa access.
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 nalalantad ang environment variables
Magtakda ng password sa environment: at mase-save ito sa configuration ng container sa disk. Makikita ito ng sinumang user na kabilang 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. Ipinapakita ito ng docker compose config sa terminal, kaya nauuwi ito sa pagkopya at pag-paste sa isang support forum. Namamana ng child process ang variable bilang default, maliban kung sadyang sinisimulan ito ng parent gamit ang kapalit na environment. Mababasa rin ito ng prosesong tumatakbo bilang parehong user ng PID 1, o may hawak ng CAP_SYS_PTRACE, mula sa /proc/1/environ. Sa container kung saan lahat ay tumatakbo bilang root, lahat ng process ay may ganitong access. Kung tumatakbo ang application bilang unprivileged user habang root ang PID 1, permission error sa file na iyon ang matatanggap nito. Hawak pa rin nito ang variable sa sarili nitong environment. Bukod dito, karaniwang inilalagay ng application crash handler ang buong environment sa log o error report.
Ang pagiging miyembro ng docker group ay katumbas ng root access sa host. Kaya hindi ito privilege boundary na maaaring asahan. Ipinapaliwanag ng gabay tungkol sa mga user account na may least privilege sa VPS kung bakit dapat limitahan ang membership 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 ginagamit ang aking .env file sa loob ng container?
Hindi ito binabalewala. Ang .env file ang nagse-substitute ng mga placeholder na ${NAME} sa compose file, at maaari rin nitong itakda ang sariling mga variable ng Compose gaya ng COMPOSE_PROJECT_NAME o COMPOSE_FILE. Hindi nito kailanman itinatakda ang mga variable sa loob ng container. Para maipasok ang value sa container, i-reference ito: environment: { KEY: "${NAME}" }, o gamitin ang env_file: ./that-file.env.
Mas nangingibabaw ba ang environment sa env_file, o kabaligtaran?
Nangingibabaw ang environment:. Ayon sa documented 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 value na gagamitin ng Compose?
Patakbuhin ang docker compose config upang i-print ang ganap na resolved na compose file matapos ilapat 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 process 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 naka-store ang source file sa host disk nang walang encryption. Ang pakinabang nito ay scope, hindi encryption: nananatiling wala ang value sa container environment, sa output ng docker inspect, at sa mga crash dump na nagpi-print ng environment.
Maaari ba akong gumamit ng quotes at spaces sa env file?
Oo. Tinatanggal ng Compose ang nakapaligid na quotes, kaya parehong nagbibigay ang KEY="value with spaces" at KEY=value with spaces ng value with spaces. Gumamit ng single quotes kapag may value na naglalaman ng $ na hindi dapat i-interpolate, at i-quote ang anumang value na naglalaman ng # dahil nagsisimula ito ng comment kapag hindi naka-quote. Tinatanggal ang spaces sa paligid ng =, kaya itinatakda pa rin ng KEY = value ang KEY.