Nginx Environment Variables with Docker
Learn to use environment variables inside an Nginx docker container using docker compose and envsubst.
Table of Contents 📖
- Nginx and Environment Variables
- Project Setup
- How Environment Variable Substitution Works
- Nginx Template Configuration
- Running envsubst Again
Nginx and Environment Variables
Nginx doesn't support environment variables by default. However, docker provides Nginx images that support environment variables since version 1.19 by using the envsubst command. The envsubst command searches input for the pattern $VARIABLE or ${VARIABLE} and replaces it with the provided environment variables. This is useful as we can easily turn a Nginx configuration file from boilerplate code like this below.
server {
server_name ${REVERSE_PROXY_HOST};
listen ${REVERSE_PROXY_PORT};
location / {
proxy_pass http://${SERVER_HOST}:${SERVER_PORT}/;
proxy_http_version 1.1;
}
}
To a legitimate configuration file like this.
server {
server_name reverse-proxy;
listen 6001;
location / {
proxy_pass http://server:6002/;
proxy_http_version 1.1;
}
}
This also makes it very easy to toggle between different environments like development and production.
Project Setup
To demonstrate, we will be using Nginx as a reverse proxy for a Node application server. We'll do this with docker compose.
version: '3.9'
services:
# Node
server:
image: server
container_name: ${SERVER_HOST}
build:
context: server
dockerfile: Dockerfile
env_file: .env
ports:
- ${SERVER_PORT}:${SERVER_PORT}
volumes:
- ./server/src:/server/src
- server-v-node-modules:/server/node_modules
# Nginx
reverse-proxy:
image: reverse-proxy
container_name: ${REVERSE_PROXY_HOST}
build:
context: reverse-proxy
dockerfile: Dockerfile
env_file: .env
restart: always
ports:
- ${REVERSE_PROXY_PORT}:${REVERSE_PROXY_PORT}
volumes:
- ./reverse-proxy/default.conf.template:/etc/nginx/templates/default.conf.template
depends_on:
- server
volumes:
server-v-node-modules:
name: server-v-node-modules
Here we have two servies: one called server which is a Node server and one called reverse-proxy which is an nginx reverse proxy.
How Environment Variable Substitution Works
In this setup, the line that is important to us is the volume for Nginx. This sets up a volume between a file called default.conf.template on our host machine and the location /etc/nginx/templates/default.conf.template in the container.
volumes:
- ./reverse-proxy/default.conf.template:/etc/nginx/templates/default.conf.template
The location inside the container is important as this where the Nginx docker image performs its environment variable replacement. Specifically, the envsubst function in the docker image reads template files (any file ending in .template) in /etc/nginx/templates and outputs the results to /etc/nginx/conf.d. This is why we set up a volume into /etc/nginx/templates. The configuration files (files ending in .conf) of conf.d ultimately end up being imported into the main Nginx configuration file nginx.conf.
include /etc/nginx/conf.d/*.conf;
Nginx Template Configuration
To use environment variables in the Nginx docker image, simply use the dollar sign and curly braces syntax.
server {
server_name ${REVERSE_PROXY_HOST};
listen ${REVERSE_PROXY_PORT};
location / {
proxy_pass http://${SERVER_HOST}:${SERVER_PORT}/;
proxy_http_version 1.1;
}
}
For example, here we have 4 environment variables: REVERSE_PROXY_HOST, REVERSE_PROXY_PORT, SERVER_HOST, and SERVER_PORT. These will all be replaced by environment variables with the envsubst command. For example, after substitution it would look like this.
server {
server_name reverse-proxy;
listen 6001;
location / {
proxy_pass http://server:6002/;
proxy_http_version 1.1;
}
}
Also, the template extension will be stripped from the file and the output will be placed inside /etc/nginx/conf.d. Therefore, the name will go from default.conf.template to default.conf. After this, because the file now ends in .conf, it will be imported into the main Nginx configuration file nginx.conf. To ultimately get these environment variables imported by Nginx, we need to specify them inside docker-compose.yaml.
env_file: .env
The env_file attribute adds environment variables to a container. In our configuration, we add environment variables to both our Node and Nginx service. This is the contents of our .env file.
REVERSE_PROXY_HOST=reverse-proxy
REVERSE_PROXY_PORT=6002
SERVER_HOST=server
SERVER_PORT=6001
Note that we also use these environment variables inside our docker-compose.yaml file. These are supplied by the docker compose command and specify --env-file. Lets do this now and set up these containers and images.
docker compose --env-file .env up
...
reverse-proxy | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
reverse-proxy | 20-envsubst-on-templates.sh: Running envsubst on /etc/nginx/templates/default.conf.template to /etc/nginx/conf.d/default.conf
If we look at the output, we can see envsubst being ran against our template file. This is listed after docker-entrypoint.sh which is ran when a container starts from an image.
Running envsubst Again
Because envsubst is ran when the container starts up, we also need a way to run it when the container is already running. This way we can change our env variables and reload our Nginx configuration without shutting down the containers.
docker exec reverse-proxy sh -c "envsubst < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -s reload"
This command runs envsubst against our template file and then reloads our Nginx container with an updated configuration. However, note that if we want to change the environment variables we need to re-run docker compose again because the env values are placed in the container with the env_file attribute in docker-compose.yaml.