DEV Community

Robin Kretzschmar
Robin Kretzschmar

Posted on

Set docker build args from .env file (NextJS)

I recently came across the issue that I had to build a Docker container with a NextJS app inside that was relying on a environment variable to set the domain for Plausible.io.
The tricky thing was that I was building the container once and deploying it to multiple pods with different environment configs.

Technically docker can respect a .env file when it is run with the option --env-file like so:

docker run --rm --env-file .env -p 3000:3000 -d YOUR_CONTAINER_TAG:prod
Enter fullscreen mode Exit fullscreen mode

So here comes the issue...

But since the environment was set during build time inside the container and NextJS has automatic site optimiziation and builds pre-rendered pages for server-side render during build, the environment variables from the Docker build run were baked into the image and it did not care about the .env file for server logic.

A small bash script to the rescue

I am using Envault to manage environment configs and since the image is being build with a Jenkins pipeline, the pipeline pulls the correct .env file from Envault each time.
Inside the Dockerfile, change the vars to be this:

ARG DB_USER
ARG DD_SVC_NAME
ENV DB_USER=$DB_USER
ENV DD_SVC_NAME=$ARG DD_SVC_NAME
Enter fullscreen mode Exit fullscreen mode

This means the build command needs to contain --build-arg paramters for each variable.
To read those from the .env file via bash, we can use this:

$(for i in `cat .env`; do out+="--build-arg $i " ; done; echo $out;out="")
Enter fullscreen mode Exit fullscreen mode

The docker command looks like this:

docker build -f Dockerfile -t MYTAG:prod $(for i in `cat .env`; do out+="--build-arg $i " ; done; echo $out;out="") .
Enter fullscreen mode Exit fullscreen mode

Bonus round: makefile

Since I love Makefiles, there is a small pitfall how to include the bash loop into the command, so here is my Makefile for reference:

SHELL := /bin/bash
...
# this line will set the build args from env file
DECONARGS = $(shell echo "$$(for i in `cat .env`; do out+="--build-arg $$i " ; done; echo $$out;out="")")
GEN_ARGS = $(eval BARGS=$(DECONARGS))

.PHONY: dist-prod
dist-prod:
    @echo "Running docker build for PROD ..."
        $(GEN_ARGS)
    docker build -f Dockerfile -t $(TAG_SHA) $(BARGS) .
...
Enter fullscreen mode Exit fullscreen mode

Top comments (5)

Collapse
 
naucode profile image
Al - Naucode

That was a nice read! Liked, bookmarked and followed, keep the good work!

Collapse
 
darksmile92 profile image
Robin Kretzschmar

Thanks! :)

Collapse
 
akshit profile image
Akshit

I need to promote the same image from say dev environment to staging environment. Can you help me in this?
Even if I use env defined variables from vault, vars will be environment specific and will require to re-trigger image build, right?

Thanks

Collapse
 
nhm1990 profile image
nhm1990

Thanks! But as it seems we have no other way to solve it (passing build-arg's is also recommended by docker).

I've been looking for hours how to optimise the process of my Azure pipeline.... The Docker concept "Build the Docker image only once, push it to the registry only once, deploy it anywhere and anytime." is not working in my case....

Greetings from Ludwigshafen! ;-)

Collapse
 
king11 profile image
Lakshya Singh

Thanks for this helped me solve the problem