Environment variables in NGINX configs inside Docker feature image

Environment variables in NGINX configs inside Docker

For quite some time now, I've been looking at some courses related to Docker and DevOps in general. Mostly because I want to deploy a varety of apps using different stacks within the same cloud instance but also because I have to use Docker at work more. Anyway, I've found a decent course, which I'll review some time soon, but while going through it I faced an issue.

The course project uses docker-compose to spin up several services: - frontend - api - auth - proxy/http server (NGINX)

I won't go into too much of a detail in terms of project structure and so on, instead I'll focus on the actual issue I faced.

As I don't like to hardcode values such as host, ports, etc, I usually store them in .env file. Which is exactly what I did pretty much accross the board within my docker-compose.yml. However, I didn't exactly work with NGINX as it doesn't support environment variable out of the box.

After spending some time googling, I realized that this issue is not as trivial as it seemed.

The image I used was nginx:stable-alpine. After reading official docs, I found out that there is a built-in function called envsubst that is supposed to replace environment variables in nginx config for their actual values.

so for example there is something like this in docker-compose:

#...nginx config
nginx:
    build: ./nginx
#...
environment:
    - NGINX_HOST=${NGINX_HOST}
    - NGINX_PORT=${NGINX_PORT}
#...

and your nginx config is something like:

server {
    listen ${NGINX_PORT}
    server_name ${NGINX_HOST}
    #....
}

In theory, if you use Nginx config template, the function is supposed to replace the environment variables. However, it didn't work for me. Turned out, it replaced them with an empty space. It would work if I declared the variables directly in the docker-compose file but I don't like this approach for several reasons.

Reading this post by Nick Janetakis reminded me that I actually need to export variables so I can use them. I thought no problem I'd just copy my .env file to docker container and run source command. But hang on, this image doesn't come with bash... No problem, I thought, lets define a Dockerfile for Nginx and install bash:

FROM nginx:stable-alpine

RUN apk add --no-cache bash

Then you need to execute a command that runs envsubst function. And it works, but it also completely wipes Nginx variables like $http_upgrade. After digging some more I found a solution (Apologies to the author as I don't remebmer where on GitHub I found it):

#nginx config
    volumes:
      - ./nginx/configs/nginx.conf.prod:/etc/nginx/templates/default.conf.template
      - .env:/etc/nginx/environment/.env
    environment:
      - NGINX_HOST=${PROD_NGINX_HOST}
      - NGINX_PORT=&{NGINX_PORT}
    command: >
      bin/bash -c "source /etc/nginx/environment/.env
      && envsubst \"`for v in $$(compgen -v);do printf '$${%s} ' $$v;done`'\" < /etc/nginx/templates/default.conf.template > etc/nginx/conf.d/nginx.conf && nginx -g 'daemon off;'"
#....

Basically, I copy .env file and nginx config to docker as default.conf.template. Then I export all the variables from .env. After that, I create new nginx config from template using envsubst folder. The wierd command between the function name and template allows me to keep variables like $http_upgrade.

I hope this will help someone!

BACK TO BLOG