Passing environment variables into a Dockerised NGINX configuration
I was recently working on a small project that required environment variables to be passed into an NGINX Docker container from the host system. The environment variables had to be available to the NGINX config at runtime, so that changes would show up whenever the container was rebuilt with a new value.
I quickly discovered that NGINX configs don't work quite that way straight out of the box, so after a bit of research and experimentation I decided to document my solution and hopefully save someone else the same trouble.
By the end of this article, you should be able to:
- build a Dockerised NGINX config that uses values passed in from environment variables
- test the functionality of the config using Docker
- Docker version 17.12.0-ce or higher
Decisions: Lua or Perl
There are actually a couple of different ways to do the same thing here.
If you plan on using Lua, then NGINX seems to recommend that you use the lua-nginx-module available in one of the OpenResty releases which, as stated in the NGINX docs, "....integrate Nginx, ngx_lua, LuaJIT 2.1, as well as other powerful companion NGINX modules and Lua libraries".
I know nothing about Lua, and as my needs were very simple it didn't seem to make sense to stray too far from my tried and trusted NGINX setup, and invest the time in learning about the openresty implementation. On the other hand, if time isn't a factor for you, or you have experience with Lua, then by all means check out what openresty has to offer as it may well have advantages over the Perl module.
With this in mind, I decided that it might be easier to use my existing, though very limited, knowledge of Perl, and instead make use of the ngx_http_perl_module.so that's provided in the 1.13.8-alpine-perl container from NGINX.
Crack open the code
Take a look at my source code, and I'll explain the main points below:
The exact structure of your Dockerfile will vary depending upon the project that you're building, so I won't bother going into the details of this. Just notice that I'm using nginx:1.13.8-alpine-perl as a base container....
....and storing my config in /etc/nginx/nginx.conf
This is the file that we're really interested in (/etc/nginx/nginx.conf).
First of all, I tell NGINX to load the "ngx_http_perl_module.so" module. This module is baked into the nginx:1.13.8-alpine-perl image that I used as the base for my Dockerfile, so it can be loaded up straight away without any hassle.
The next line of interest is where I create the environment variable that exists within the NGINX container. Out of the box, NGINX provides support for environment variables, but strips away anything that is passed in from outside. I'll deal with that problem later on, but I still need to create a variable to hold the value internally.
I can now use Perl's perl_set command to grab the value of the environment variable that I'm passing in from outside (when the Docker image is built), and assign it to a variable in the NGINX config.
Finally, I return the value that is stored within the $happy_greeting variable as part of the response from an http route:
Passing values with Docker
Now it's time to use Docker to build an image and spin up a container, so that we can see some proof that all of this actually works.
If you've cloned my repo from Github, then you can open a terminal in the project root and run the following command:
Run the following command, and you should now see an image named nginx-config-env-vars listed:
There are a couple of different ways to pass environment values into a Docker container, but I like to create a file and pass them in from there.
In the project root, you'll notice that there's a file named env.dev. This contains the environment variable and the value that we'll be passing in.
Run the following command to spin up the container:
Point your browser at localhost, and you should see a message displayed as shown below:
If we change the value of the environment variable and then rebuild the container, we can see that our NGINX config responds to the changes.
First, stop our running container:
Then remove the stopped container:
Change the contents of the env.dev file, so that the HAPPY_GREETING variable is assigned a different value:
If you bring up the container again....
....then you should see that the message has changed when you point your browser at localhost:
With any luck, you now have a working example of a Dockerised NGINX config that will respond to values passed in from the outside.
As I've mentioned, this wasn't the only way that I could've acheived my goal, so have a look around and find the solution that works best for you. Hopefully this article has at least helped you toward that decision though.