Description
Description
The way .env
files are loaded has changed twice recently.
Let's assume the following project structure:
$ tree -a
.
├── .env
├── compose.yaml
└── sub
└── .env
When running docker compose
in the sub/
directory, ...
- up to Compose 2.23.3, Compose merges both
.env
files, with the top-level.env
file taking precedence over the sub-level one - from Compose 2.24.0 to 2.24.5, Compose only uses the
.env
file in the current directory, and ignore the one at the top level - from Compose 2.24.6 to at least 2.27.0, Compose only uses the
.env
file at the top-level directory, and ignores the one in the sub directory
Both changes broke some folks' workflows in different ways (see #11531 and #11575). The second change might be related to #11405 but I haven't looked at the code, so I might be wrong.
Steps To Reproduce
cd /tmp
mkdir -p envtest envtest/sub
cat >envtest/compose.yaml << "EOF"
services:
echo:
image: alpine
command: echo $ROOT $SUB win=$WIN
EOF
cat >envtest/.env << EOF
ROOT=root
WIN=root
EOF
cat >envtest/sub/.env << EOF
SUB=sub
WIN=sub
EOF
cd envtest/sub
docker compose run echo
Here is the output with various versions of Compose...
Up to Compose 2.23.3:
root sub win=root
From Compose 2.24 to 2.24.5:
WARN[0000] The "ROOT" variable is not set. Defaulting to a blank string.
sub win=sub
From Compose 2.24.6:
WARN[0000] The "SUB" variable is not set. Defaulting to a blank string.
root win=root
Compose Version
N/A
Docker Environment
N/A
Anything else?
The ability of loading a .env
file from the current directory (instead, or in addition to, the top-level .env
file) is very powerful.
Example user story: I develop an application stack made of 2 services, service1
and service2
. I sell that stack as a SAAS to multiple customers. Each customer gets their own stack, with their own instance of each service. I want to deploy each stack with Compose and follow "DRY" principles: I want to avoid repeating or duplicating code as much as possible.
I can achieve that with the following structure:
.
├── compose.yaml
├── customers
│ ├── customer1
│ │ └── .env
│ ├── customer1
│ │ └── .env
│ └── customer3
│ └── .env
├── service1
│ └── Dockerfile
└── service2
└── Dockerfile
The .env
files in the customerX
directories set customer-specific information (credentials, plan limits, etc.) and they also set COMPOSE_PROJECT_NAME
to a unique value.
That way, to manage the stack for customerX
, all I have to do is go to their specific directory and run docker compose
command. I don't need to set environment variables - and more importantly, I don't risk forgetting to set a critical environment variable or command-line flag before manipulating a customer environment.