Published on

Using Different .env files with create-react-app and Docker

Authors

The Issue

React projects that are built with create-react-app have support for .env files by default. This setup also allows different configurations to be used for different environments. In my project I had an .env.production file that looked like the following:

PUBLIC_URL=ui
port=3001

I also had a .env.local file that looked as follows:

PUBLIC_URL=
port=3001

This properties file controls which port React will use as well as what to prefix all routes with. With the production properties, all routes would be prefixed with ui/.

The problem was I needed this only in production but not for local builds and dev. So if I ran npm run build locally I did not want the PUBLIC_URL to have any value. I wanted this run from inside a docker container so that I could easily run all the pieces of my app together and I had a makefile to simplify this whole process.

After some digging I found that create-react-app allows environment specific config by creating environment specific files where the file name for a specific environment is basically .env.${NODE_ENV}. For example production is .env.production. This page describes exactly which .env files can be used as well as the order of precedence.

Ok, awesome so I simply need to override NODE_ENV when I run the build in the dockerfile locally:

NODE_ENV=local npm run build

But this did not work :/

Overriding .env files with Create-react-app

After reading the create-react-app documentation more carefully I noticed this line which I had missed:

You cannot override NODE_ENV manually. This prevents developers from accidentally deploying a slow development build to production.

This explained my issue. I dug around a bit more and found out about the tool env-cmd which lets you override this behaviour in react where I simply change the above command to:

cmd-env .env.local npm run build

I went into my build directory and used python SimpleServer to test if it worked:

python -m http.server 3002

When I hit the page I got a blank screen and on looking at the network panel I noticed that I was getting 404's as the app was trying to use a PUBLIC_URL of ui despite the fact that my .env.local had nothing for this. It seems like it merges values if they are empty. I looked through the env-cmd file and settled on using an .env-cmdrc file instead which looked as follows:

{
  "local": {},
  "production": {
    "PUBLIC_URL": "ui",
    "PORT": "3002"
  }
}

I then simply run the following command:

cmd-env local npm run build

After checking using python's simple server I confirmed this approach worked perfectly when using local and when using production as parameters.

Passing the Environment to be Used to a Dockerfile

On the Docker side, I now need to pass this environment variable in and get the dockerfile to use it at build time. To pass the environment variable in I updated my dockerfile as follows:

...
ARG env
ENV environment=$env
RUN echo "---------------env ${environment}"
...
RUN cd /root/my-ui && \
env-cmd ${environment} npm install && \
env-cmd ${environment} npm run build
...

Docker allows you to pass environment variables using a command similar to the below:

docker build --build-arg env=local -t your.domain.com/yourOrg/project:latest . -f ./path/to/Dockerfile

Passing an Environment Variable to a Makefile

Finally I updated my makefile to set the environment to production by default but allow it to be overridden as follows:

...
ENVIRONMENT?=production
...
docker-build-app-1: ## Build the app1's docker images
docker build --build-arg env=local -t your.domain.com/yourOrg/project:latest . -f ./path/to/Dockerfile

Now if I run make docker-build-app-1 it will use production as the default environment. To use another environment I pass in an updated environment variable to the makefile as follows:

make docker-build-app-1 ENVIRONMENT=local