Skip to content

[BUG] Regressions in .env loading #11823

Closed
@jpetazzo

Description

@jpetazzo

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.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions