Terminus
How To Use An .env File In Docker Compose

How To Use An .env File In Docker Compose

The short answer

In Docker Compose, environment variables can be defined in the form of a list of key-value pairs in a file named [.inline-code].env[.inline-code] located in the root directory of your project:

 # .env

VERSION=v1.5

These variables can then be referenced in the [.inline-code]compose.yaml[.inline-code] file using the variable substitution syntax as follows:

 # compose.yaml

version: '3'

services:
  web:
    image: "webapp:${VERSION}"

In this example, upon execution of the [.inline-code]docker compose up[.inline-code] command, Docker will substitute the [.inline-code]VERSION[.inline-code] variable with its value, resulting in the pulling of the [.inline-code]webapp:v1.5[.inline-code] image. Note that, if the specified environment variable is not set, Docker will substitute the variable with an empty string.

[#use-custom-env-files] Using custom [.inline-code].env[.inline-code] files [#use-custom-env-files]

Complex projects often include multiple environment files for different purposes, like development (e.g. [.inline-code].env.dev[.inline-code]) and production (e.g. [.inline-code].env.prod[.inline-code]).

In Compose, to use a specific environment file instead of the default [.inline-code].env[.inline-code] file, you can use the [.inline-code]docker compose up[.inline-code] command with the [.inline-code]--env-file[.inline-code] flag as follows: 

 $ docker compose --env-file <file> up 

Where:

  • [.inline-code]file[.inline-code] is the absolute or relative path to the environment file.

For example:

 $ docker-compose --env-file ./config/.env.dev up

Upon execution, the above command will bring up the services set in the [.inline-code]compose.yaml[.inline-code] file and substitute the values of the environment variables from the [.inline-code].env.dev[.inline-code] file.

[#understand-the-variable-precedence] Understanding the order of precedence of environment variables [#understand-the-variable-precedence]

In Compose, the same environment variables can be defined in multiple places, like the shell environment, the default [.inline-code].env[.inline-code] file, or an arbitrary environment file (e.g. [.inline-code].env.prod[.inline-code]).

The order of precedence of these variables is as follows, from the highest to the lowest:

  1. Shell environment: The variables set in your shell environment take the highest priority. Any variables set in the shell environment will always override those in your environment files. 
  2. Custom [.inline-code].env[.inline-code] file: The environment variables loaded using the [.inline-code]--env-file[.inline-code] flag takes precedence over the default [.inline-code].env[.inline-code] file located at the root of your project's directory. 
  3. Default [.inline-code].env[.inline-code] file: The variables set in the default [.inline-code].env[.inline-code] file have the lowest precedence compared to the above sources.

[#pass-environment-variables-to-containers] Passing environment variables to services [#pass-environment-variables-to-containers]

In Compose, to pass environment variables defined in the [.inline-code].env[.inline-code] file to the various services defined in the [.inline-code]compose.yaml[.inline-code] file, you can use the [.inline-code]environment[.inline-code] property as follows:

 environment:
  <name>:  ${VARIABLE}

Where:

  • [.inline-code]name[.inline-code] specifies the name of the environment variable for the container.
  • [.inline-code]VARIABLE[.inline-code] specifies the name of the environment variable defined in the [.inline-code].env[.inline-code] file.

For example:

 # .env

PG_USERNAME="admin"
PG_PASSWORD="superadmin"
PG_DATABASE="shop"
 # compose.yaml

version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: ${PG_USERNAME}
      POSTGRES_PASSWORD: ${PG_PASSWORD}
      POSTGRES_DB: ${PG_DATABASE}
    volumes:
      - ./data:/var/lib/postgresql/data
    ports:
      - 5432:5432

Upon execution of the [.inline-code]docker compose up[.inline-code] command, Compose will start the [.inline-code]web[.inline-code] service and substitute the value of variables [.inline-code]PG_USERNAME[.inline-code], [.inline-code]PG_PASSWORD[.inline-code], and [.inline-code]PG_DATABASE[.inline-code] with the values defined in the [.inline-code].env[.inline-code] file.

You can learn more about PostgreSQL with our article on how to launch a PostgreSQL instance using Docker Compose.

[#easily-recall-syntax-with-ai] Easily retrieve this syntax using Warp AI feature [#easily-recall-syntax-with-ai]

If you’re using Warp as your terminal, you can easily retrieve this syntax using the Warp AI feature:

Entering [.inline-code]docker compose env file[.inline-code] in the AI question input will prompt a human-readable step by step guide including code snippets.

Passing [.inline-code].env[.inline-code] files to services

In Compose, to pass an entire environment file to a service listed in the [.inline-code]compose.yaml[.inline-code] file, you the [.inline-code]env_file[.inline-code] property as follows:

 env_file:
  - <file>

Where:

  • [.inline-code]file[.inline-code] is the absolute or relative path to the environment file.

For example:

 # compose.yaml

version: '3'

services:
  web:
    build: .
    env_file:
     - ./.env.dev
     - ./.env.prod

Upon execution of the [.inline-code]docker compose up[.inline-code] command, Compose will start the [.inline-code]web[.inline-code] service and forward all the environment variables defined in the [.inline-code].env.dev[.inline-code] and [.inline-code].env.prod[.inline-code] files.

Note that since environment files are loaded sequentially, some variables may potentially be overwritten if already defined in preceding files.

Also note that environment variables defined under the [.inline-code]environment[.inline-code] have a higher precedence than the ones passed under the [.inline-code]env_file[.inline-code] property.

[#define-default-variable-values] Defining default values for environment variables [#define-default-variable-values]

In Compose, to assign a default fallback value to an unset or empty environment variable, you can use either one of the following variable substitution expressions:

 ${VAR:-default}
${VAR-default}

Where:

  • [.inline-code]${VAR:-default}[.inline-code] is used to indicate that the [.inline-code]default[.inline-code] value should be used when the variable [.inline-code]VAR[.inline-code] is empty or unset. 
  • [.inline-code]${VAR-default}[.inline-code] is used to indicate that the [.inline-code]default[.inline-code] value should be used only when the variable [.inline-code]VAR[.inline-code] is not set.

For example:

 # .env
NGINX_VERSION=1.19.5
CONTAINER_PORT=
 # compose.yaml

version: '3'

services:
  web:
    image: "nginx:${NGINX_VERSION:-latest}"
    environment:
      - DATABASE_URL=${DB_URL:-mysql://user:password@dbhost/dbname}
    ports:
      - ${CONTAINER_PORT:-8080}

Upon execution of the [.inline-code]docker-compose up[.inline-code] command, the environment variables will be set as follows:

  • [.inline-code]NGINX_VERSION[.inline-code] is set and non-empty in the [.inline-code].env[.inline-code] file, so the specified value [.inline-code]1.19.5[.inline-code] will be used.
  • [.inline-code]DB_URL[.inline-code] is not set in the [.inline-code].env[.inline-code] file, so the default value [.inline-code]mysql://user:password@dbhost/dbname[.inline-code] will be used.
  • [.inline-code]CONTAINER_PORT[.inline-code] is set but is empty in the [.inline-code].env[.inline-code] file, so the default value [.inline-code]8080[.inline-code] will be used.

[#enforce-required-variable-values] Enforcing required values for environment variables [#enforce-required-variable-values]

In Compose, to enforce the existence or definition of a required environment variable, which is essential for the proper functioning of the application, you can use either one of the following variable substitution expressions:

${VAR:?error}
${VAR?error}

Where:

  • [.inline-code]${VAR:?error}[.inline-code] will exit with the specified error message [.inline-code]error[.inline-code] if the variable [.inline-code]VAR[.inline-code] is empty or unset. 
  • [.inline-code]${VAR?error}[.inline-code] will exit with the specified error message [.inline-code]error[.inline-code] only if the variable [.inline-code]VAR[.inline-code] is not set.

For example: 

 # .env

TAG=v1.5
CONTAINER_PORT=
 # compose.yaml

version: '3'

services:
  web:
    image: "nginx:${TAG:?Error: TAG is required}"
    environment:
      - API_KEY=${API_KEY:?Error: API_KEY is required}
    ports:
      - ${CONTAINER_PORT:?Error: Container Port is required}
      - ${CONTAINER_PORT?Error: Container Port is required}

Upon execution of the [.inline-code]docker-compose up[.inline-code] command, the environment variables will be set as follows: 

  • [.inline-code]TAG[.inline-code] is set and non-empty in the .env file, so the specified value [.inline-code]v1.5[.inline-code] will be used. 
  • [.inline-code]API_KEY[.inline-code] is not set in the .env file, so the command will exit with the error message [.inline-code]Error: API_KEY is required[.inline-code]. 
  • [.inline-code]CONTAINER_PORT[.inline-code] is set but is empty in the .env file. 
  • With the colon ([.inline-code]:[.inline-code]) syntax, the empty value for the [.inline-code]CONTAINER_PORT[.inline-code] will be used. 
  • Without the colon ([.inline-code]:[.inline-code]) syntax, the command will not exit, and the empty value for the [.inline-code]CONTAINER_PORT[.inline-code] will be used.

[#replace-existing-variable-values] Replacing environment variable values [#replace-existing-variable-values]

In Compose, to allow your application to dynamically switch between different configurations based on the availability of specific values, you can overwrite the value of an existing or non-empty environment variable using either one of the following variable substitution expressions:

${VAR:+replacement}
${VAR+replacement}

Where:

  • [.inline-code]${VAR:+replacement}[.inline-code] replaces the value of the [.inline-code]VAR[.inline-code] variable with [.inline-code]replacement[.inline-code] if the variable is set and non-empty. 
  • [.inline-code]${VAR+replacement}[.inline-code] replaces the value of the [.inline-code]VAR[.inline-code] variable with [.inline-code]replacement[.inline-code] only if the variable is set. 

For example: 

 # .env

TAG=v1.5
CONTAINER_PORT=
# compose.yaml

version: '3'

services:
  web:
    image: "nginx:${TAG:+latest}"
    environment:
DB_URL=${DB_URL:+mysql://user:password@dbhost/dbname}
    ports:
      - ${CONTAINER_PORT:+8080}
      - ${CONTAINER_PORT+8080}

Upon execution of the [.inline-code]docker-compose up[.inline-code] command, the environment variables will be set as follows:

  • [.inline-code]TAG[.inline-code] is set and non-empty in the .env file, so the replacement value [.inline-code]latest[.inline-code] will be used.
  • [.inline-code]DB_URL[.inline-code] is not set in the .env file, so an empty value will be used for [.inline-code]DB_URL[.inline-code]. 
  • [.inline-code]CONTAINER_PORT[.inline-code] is set but empty in the .env file. 
  • With the colon ([.inline-code]:[.inline-code]) syntax, the empty value for the [.inline-code]CONTAINER_PORT[.inline-code] will be used. 
  • Without the colon ([.inline-code]:[.inline-code]) syntax, the replacement value [.inline-code]8080[.inline-code] will be used.