What is the best practice for storing environment variables in a Docker project?

I have 3 containers for a project: nginx, mongodb and nextjs.

Some variables are mutual for nginx and nextjs, other variables are mutual for mongodb and nextjs.

I have three different strategies for storing environment variables.

1. Storing all env variables in .env

I can store all my env vars inside the roots .env where my docker-compose.yml is stored. Docker will automatically include all the env variables after I run docker compose for my project.

All the variables will be available on the host and in each container.

2. Separate .env for each container

In this case all my env vars are isolated inside a container.

for mongodb:

env_vars:
 - ./mongodb/.env

for nextjs:

env_vars:
 - ./nextjs/.env

etc

No exposure for the host but repeated env vars between .env files.

3. One .env file and setting env vars in docker-compose.yml

I can set env variables from `environment

for mongodb:

environment:
  - MONGO_URI=${MONGO_URI}

for nextjs:

environment:
 - NEXTJS_HOST=${NEXTJS_HOST}
 - NEXTJS_MONGO_URI=${MONGO_URI}

etc

All env vars are set in the host. No need to repeat yourself. Only one file for all containers.

4. General .env and separate container based .envs

General env variables in .env in the root. And more particular envs in the container’s .env file.

For example:

env_file:
  - ./nextjs/.env
  - ./.env

My approach is fourth. But it feels that I need to switch to another approach as it is getting to cluttered and hard to maintain. I want to move to the first approach.

What is the best practice from security point of view? From convenience point of view?

I’m not sure how any of the solutions you described have anything to do with security. The env files are readable and unencrypted in every case. and despite what you wrote in

there is nothing in your description that indicates that the variables are set on the host…

The right solution depends on what you need. I can’t imagine a “best practice” that works for everyone. You also wrote

I don’t see where you repeated yourself except above your quoted statement. Sine you repeated the variable names.

Whenever you have variables that ae absolutely for a single container and there is no common value, you can use env_file. If you can easily categorize your variables to have a common env file and separate files for specific containers, you can do that. When you have complex configurations and some values are part of many variables in multple containers, you probably want a .env file read by compose automatically (not defined in env_file, but you can also have multiple env files for variables in the compose file using the ``–env-file` parameter of the docker compose command.

Then you can define variables that you can refer to in the compose files even in the environment section and define variables like:

environment:
  - DOMAIN=${SERVICE1_SUBDOMAIN}.${BASE_DOMAIN}

When it becomes even more complicated, you will find a template system useful. I for example generate compose files sometimes using Ansible which uses the Jinja template language. Then it suddenly doesn’t matter where your variables are in the compose project.