How to use environment variables in docker compose
The best way is to specify environment variables outside the docker-compose.yml
file. You can use env_file
setting, and define your environment file within the same line. Then doing a docker-compose up again should recreate the containers with the new environment variables.
Here is how my docker-compose.yml looks like:
services:
web:
env_file: variables.env
Note: docker-compose expects each line in an env file to be in
VAR=VAL
format. Avoid usingexport
inside the.env
file. Also, the.env
file should be placed in the folder where the docker-compose command is executed.
- Create a
template.yml
, which is yourdocker-compose.yml
with environment variable. - Suppose your environment variables are in a file 'env.sh'
- Put the below piece of code in a sh file and run it.
source env.sh; rm -rf docker-compose.yml; envsubst < "template.yml" > "docker-compose.yml";
A new file docker-compose.yml
will be generated with the correct values of environment variables.
Sample template.yml file:
oracledb:
image: ${ORACLE_DB_IMAGE}
privileged: true
cpuset: "0"
ports:
- "${ORACLE_DB_PORT}:${ORACLE_DB_PORT}"
command: /bin/sh -c "chmod 777 /tmp/start; /tmp/start"
container_name: ${ORACLE_DB_CONTAINER_NAME}
Sample env.sh file:
#!/bin/bash
export ORACLE_DB_IMAGE=<image-name>
export ORACLE_DB_PORT=<port to be exposed>
export ORACLE_DB_CONTAINER_NAME=ORACLE_DB_SERVER
It seems that docker-compose has native support now for default environment variables in file.
all you need to do is declare your variables in a file named .env
and they will be available in docker-compose.yml.
For example, for .env
file with contents:
MY_SECRET_KEY=SOME_SECRET
IMAGE_NAME=docker_image
You could access your variable inside docker-compose.yml
or forward them into the container:
my-service:
image: ${IMAGE_NAME}
environment:
MY_SECRET_KEY: ${MY_SECRET_KEY}
The DOCKER solution:
Docker-compose 1.5+ has enabled variables substitution: https://github.com/docker/compose/releases
The latest Docker Compose allows you to access environment variables from your compose file. So you can source your environment variables, then run Compose like so:
set -a
source .my-env
docker-compose up -d
For example, assume we have the following .my-env
file:
POSTGRES_VERSION=14
(or pass them via command-line args when calling docker-compose
, like so: POSTGRES_VERSION=14 docker-compose up -d
)
Then you can reference the variables in docker-compose.yml
using a ${VARIABLE}
syntax, like so:
db:
image: "postgres:${POSTGRES_VERSION}"
And here is more info from the docs, taken here: https://docs.docker.com/compose/compose-file/#variable-substitution
When you run docker-compose up with this configuration, Compose looks for the POSTGRES_VERSION environment variable in the shell and substitutes its value in. For this example, Compose resolves the image to postgres:9.3 before running the configuration.
If an environment variable is not set, Compose substitutes with an empty string. In the example above, if POSTGRES_VERSION is not set, the value for the image option is postgres:.
Both $VARIABLE and ${VARIABLE} syntax are supported. Extended shell-style features, such as ${VARIABLE-default} and ${VARIABLE/foo/bar}, are not supported.
If you need to put a literal dollar sign in a configuration value, use a double dollar sign ($$).
The feature was added in this pull request.
Alternative Docker-based solution: Implicitly sourcing an env vars file through the docker-compose
command
If you want to avoid any bash wrappers, or having to source a env vars file explicitly (as demonstrated above), then you can pass a --env-file
flag to the docker-compose
command with the location of your env var file: https://docs.docker.com/compose/env-file/
Then you can reference it within your docker-compose
command without having to source it explicitly:
docker-compose --env-file .my-env up -d
If you don't pass a --env-file
flag, the default env var file will be .env
.
Note the following caveat with this approach:
Values present in the environment at runtime always override those defined inside the .env file. Similarly, values passed via command-line arguments take precedence as well.
So be careful about any env vars that may override the ones defined in the --env-file
!
The BASH solution:
I notice that Docker's automated handling of environment variables can cause confusion. Instead of dealing with environment variables in Docker, let's go back to basics, like bash! Here is a method using a bash script and a .env
file, with some extra flexibility to demonstrate the utility of env vars:
POSTGRES_VERSION=14
# Note that the variable below is commented out and will not be used:
# POSTGRES_VERSION=15
# You can even define the compose file in an env variable like so:
COMPOSE_CONFIG=my-compose-file.yml
# You can define other compose files, and just comment them out
# when not needed:
# COMPOSE_CONFIG=another-compose-file.yml
then run this bash script in the same directory, which should deploy everything properly:
#!/bin/bash
docker rm -f `docker ps -aq -f name=myproject_*`
set -a
source .env
cat ${COMPOSE_CONFIG} | envsubst | docker-compose -f - -p "myproject" up -d
Just reference your env variables in your compose file with the usual bash syntax (ie ${POSTGRES_VERSION}
to insert the POSTGRES_VERSION
from the .env
file).
While this solution involves bash, some may prefer it because it has better separation of concerns.
Note the COMPOSE_CONFIG
is defined in my .env
file and used in my bash script, but you can easily just replace {$COMPOSE_CONFIG}
with the my-compose-file.yml
in the bash script.
Also note that I labeled this deployment by naming all of my containers with the "myproject" prefix. You can use any name you want, but it helps identify your containers so you can easily reference them later. Assuming that your containers are stateless, as they should be, this script will quickly remove and redeploy your containers according to your .env file params and your compose YAML file.
Since this answer seems pretty popular, I wrote a blog post that describes my Docker deployment workflow in more depth: https://modulitos.com/blog/lets-deploy-part-1/ This might be helpful when you add more complexity to a deployment configuration, like Nginx configs, LetsEncrypt certs, and linked containers.