-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
.envfiles, with the top-level.envfile taking precedence over the sub-level one - from Compose 2.24.0 to 2.24.5, Compose only uses the
.envfile 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
.envfile 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.