Best practices while using environment files with Docker Compose

docker-compose is an amazing tool to create a full fledged environment using different docker containers. We have used the same in our case to deploy multiple environments for testing.

This article will share the best practices when it comes to using environments files inside docker-compose.yml

Approach #1 - Using env_file

Let’s create a simple docker-compose.yml file which prints a environment variable and then sleeps for sometime

docker-compose.yml

version: '2'
services:
  demo:
    image: centos:7.2.1511
    command: sh -c '/bin/echo "My name is $$NAME" && sleep 2000'
    env_file:
      - ./env.sh

Note we have used $$ instead of $, as docker-compose itself does variable substitution

env.sh

NAME=TARUN

Run the composition using docker-compose up -d && docker-compose logs demo

vagrant@vagrant:/vagrant/docker-compose-environment$ docker-compose up -d && docker-compose logs demo
Creating network "dockercomposeenvironment_default" with the default driver
Creating dockercomposeenvironment_demo_1
Attaching to dockercomposeenvironment_demo_1
demo_1  | My name is TARUN

Now let us update the content of env.sh

env.sh

NAME=TARUN2

Let’s restart the demo container using docker-compose restart demo && docker-compose logs demo

vagrant@vagrant:/vagrant/docker-compose-environment$ docker-compose restart demo && docker-compose logs demo
Restarting dockercomposeenvironment_demo_1 ... done
Attaching to dockercomposeenvironment_demo_1
demo_1  | My name is TARUN
demo_1  | My name is TARUN

First message is from our previous run and last one from the restart. As you can see the environment variable don’t get picked up during restart if the environment file is updated.

Note: This approach is not recommended if you plan to restart your containers and want updated variables to be picked up

Approach 2 - Using volume file mapping

docker-compose.yml

We update our docker-compose.yml to use file mapping instead

version: '2'
services:
  demo:
    image: centos:7.2.1511
    command: sh -c '. /env/env.sh && /bin/echo "My name is $$NAME" && sleep 2000'
    volumes:
      - ./env.sh:/env/env.sh

Now let us retry the restart scenario

vagrant@vagrant:/vagrant/docker-compose-environment$ docker-compose up -d && docker-compose logs demo
Creating network "dockercomposeenvironment_default" with the default driver
Creating dockercomposeenvironment_demo_1
Attaching to dockercomposeenvironment_demo_1
demo_1  | My name is TARUN
vagrant@vagrant:/vagrant/docker-compose-environment$ vim env.sh
vagrant@vagrant:/vagrant/docker-compose-environment$ cat env.sh
NAME=TARUN2
vagrant@vagrant:/vagrant/docker-compose-environment$ docker-compose restart demo && docker-compose logs demo
Restarting dockercomposeenvironment_demo_1 ... done
Attaching to dockercomposeenvironment_demo_1
demo_1  | My name is TARUN
demo_1  | My name is TARUN2

As you can see now, we get the updated environment variables.

Approach 3 - Using volume folder mapping

I personally prefer this instead of using file mapping. Folder mapping allow us to place multiple environment files which may be required for different scripts

docker-compose.yml

version: '2'
services:
  demo:
    image: centos:7.2.1511
    command: sh -c '. /env/env.sh && /bin/echo "My name is $$NAME" && sleep 2000'
    volumes:
      - ./env:/env

./env/env.sh

NAME=TARUN

The only change here being that we move our environment variables file env.sh to the env folder

Spacing in environment variables

There is suttle difference in Approach #1 and #2/#3, when it comes to spacing in envrionment variable values. Consider the below environment file

NAME="TARUN LALWANI"

See the different of outputs between Approach #1 and Approach #2

vagrant@vagrant:/vagrant/docker-compose-environment$ docker-compose -f docker-compose-approach1.yml up
Recreating dockercomposeenvironment_demo_1
Attaching to dockercomposeenvironment_demo_1
demo_1  | My name is "TARUN LALWANI"
^CGracefully stopping... (press Ctrl+C again to force)
Stopping dockercomposeenvironment_demo_1 ... done

vagrant@vagrant:/vagrant/docker-compose-environment$ docker-compose -f docker-compose-approach2.yml up
Recreating dockercomposeenvironment_demo_1
Attaching to dockercomposeenvironment_demo_1
demo_1  | My name is TARUN LALWANI
^CGracefully stopping... (press Ctrl+C again to force)
Stopping dockercomposeenvironment_demo_1 ... done

Note: Approach one will have double quotes " included as the part of the variable value, while #2 and #3 won’t

You can find the sample codes for this article on tarunlalwani/docker-compose-environment