Inconsistent handling of environment variables

I’m trying to learn how to use environmental variables and files and the documentation does not appear to match behavior.

given this compose file:

# environment test
services:
  env-demo:
    container_name: env-demo
    image: filebrowser/filebrowser
    user: "${test_UID}:${test_GID}"
    #env_file: manual_load.env
    #env_file: "manual_load.env"
    #env_file: /opt/docker-stacks/env-demo/manual_load.env 

If I create a file “.env” and put in it:

test_UID=1001
test_GID=1001 

Then when I run “docker compose config”:

 ** root@docker0 ** /opt/docker-stacks/env-demo ** Wed Oct 23 06:23:37
# docker compose -f compose-demo.yaml config 
name: env-demo                                                                                           
services:                                           
  env-demo:                                                                                              
    container_name: env-demo                                                                             
    image: filebrowser/filebrowser
    networks:                                       
      default: null
    user: 1001:1001                                 
networks:          
  default:            
    name: env-demo_default  

It works as expected and the variables in the “user:” parameter are substituted with “1001”.
w00t!

If I rename the file from “.env” to “manual_load.env”, and then explicitly load the file using “env_file:” it fails interestingly:

 ** root@docker0 ** /opt/docker-stacks/env-demo ** Wed Oct 23 17:06:48                                   
# docker compose -f compose-demo.yaml config                                                             
WARN[0000] The "test_UID" variable is not set. Defaulting to a blank string.                             
WARN[0000] The "test_GID" variable is not set. Defaulting to a blank string.                             
name: env-demo                                                                                           
services:                                                                                                
  env-demo:
    container_name: env-demo
    environment:
      test_GID: "1001"
      test_UID: "1001"
    image: filebrowser/filebrowser
    networks:
      default: null
    user: ':'
networks:
  default:
    name: env-demo_default  

There is now a new section “environment:” (which doesn’t appear when you use “.env”); but the variable substitution does not occur and you’re left with “user: ‘:’”
The file is clearly being parsed because the correct values show up in “environment:”.
The behavior is the same regardless of whether one uses a relative or full path.

If you specify the file on the command line with the “–env-file” parameter:

 ** root@docker0 ** /opt/docker-stacks/env-demo ** Wed Oct 23 17:12:55
# docker compose -f compose-demo.yaml --env-file manual_load.env config
name: env-demo
services:
  env-demo:
    container_name: env-demo
    environment:
      test_GID: "1001"
      test_UID: "1001"
    image: filebrowser/filebrowser
    networks:
      default: null
    user: 1001:1001
networks:
  default:
    name: env-demo_default  

It now both shows the “environment:” tag missing from .env and it performs the variable substitution.

If I explicitly add an “environment:” section in the compose file and run it without attempting to load an environment file:

 ** root@docker0 ** /opt/docker-stacks/env-demo ** Wed Oct 23 17:13:06
# docker compose -f compose-demo.yaml config
WARN[0000] The "test_UID" variable is not set. Defaulting to a blank string. 
WARN[0000] The "test_GID" variable is not set. Defaulting to a blank string. 
name: env-demo
services:
  env-demo:
    container_name: env-demo
    environment:
      test_GID: "1001"
      test_UID: "1001"
    image: filebrowser/filebrowser
    networks:
      default: null
    user: ':'
networks:
  default:
    name: env-demo_default

Obviously, there is now an “environment:” section in the config output; but no variable substitution occurs.

So, finally, the actually questions:
Is this expected behavior?
If this is expected behavior, what’s the logic?

I’m also assuming the choice of image makes no difference; but I haven’t experimented with that.

Version info:

# docker --version
Docker version 27.3.1, build ce12230

# docker compose version
Docker Compose version v2.29.7

# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04.1 LTS
Release:        24.04
Codename:       noble

env_file does NOT define which file will be used when Docker Compose interpolates variables into the YAML.
It is used to define the container’s environment to the contents of the file specified.
So, within the container, if you run env, you’ll see `the vriables within that env file

If you want to specify a custom .env file to interpolate from, you can do it like so:

docker compose --env-file custom.env [command]

or you can set the COMPOSE_ENV_FILES variable before running your compose command