Injecting Environment Variables with Docker into Static Webpages

When building a microserivce frontend it might be necessary to inject configuration parameters into the Docker container, which then are printed into a static website or even a javascript file. Many web servers, such as nginx and apache, support Server Side Includes (SSI). SSI is an old server-side web-technology, which was around for decades. SSI could be used for injecting environment variables into static websites. A full example can be found in a corresponding Github Repository.

Server Side Includes (SSI)

SSI provides a set of directives, which can be embedded in any document served by a web server. The web server can be configured to preprocess the document and also process the directives. For the stated use case the echo directive could be used. This directive can print a variable, known to the web server, into a served document.

Assume that for the web server a variable ENV_MY_NAME is configured and the SSI module is activated. Then with the following echo directive, the variable is printed into the HTML document:

<p>
Hello <span><!--#echo var="ENV_MY_NAME" --></span>!
</p>

To extend the example a bit, one could also print a value into a javascript snippet and by that initialize a variable, as shown in the following snippet:

<p>
<span id="greeting"></span>
<span><!--#echo var="ENV_MY_NAME" --></span>!
</p>
<script>
const greeting = '<!--#echo var="ENV_MY_GREETING" -->';
document.querySelector('#greeting').textContent = greeting;
</script>

Nginx

Nginx has support for SSI, it only has to by activated with the ssi on directive. Furthermore, inside the nginx config the variables known to the web server must be defined with the set directive. A full example of a nginx configuration snippet is shown below:

server 
{
location /
{
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
ssi on;
set $ENV_MY_NAME "world";
set $ENV_MY_GREETING "hello";
}
}

Bringing up nginx with this configuration would print the values hello and world at the respective locations of the echo SSI directives.

In the above nginx configuration the variable values were hardcoded to the strings hello and world. To be more useful the values of corresponding environment variables should be used instead. Nginx doesn't support environment variables directly inside a config file (most likely because of performance and security reasons). Therefore a workaround is required. Instead of providing a nginx config, a template for a config must be provided, which will be loaded by nginx at startup. When loading the template, nginx is substituting stated environment variables with their real values and stores the result as a config file (similar to a template engine in web development).

Given that the environment variables MY_NAME and MY_GREETING are set at the OS level, the following nginx template would substitue the variables with the corresponding values. The template should be stored at the /etc/nginx/templates/default.conf.template location, in order to create the /etc/nginx/conf.d/default.conf nginx config file.

server 
{
location /
{
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
ssi on;
set $ENV_MY_NAME "$MY_NAME";
set $ENV_MY_GREETING "$MY_GREETING";
}
}

An important note on the naming of the variables is, that the name of the nginx variable should not be the same as an environment variable. The template processor would substitute all environment variables with the corresponding value. Thus, the following example would not work as expected. As at both places $MY_NAME would be substituted with the environment variable value:

set $MY_NAME "$MY_NAME";

Docker Setup

To create a full example, one could create the following files inside a directory. The index.html and the nginx.conf would contain the content mentioned above.

 - Dockerfile
- nginx.conf
- www/index.html

The Dockerfile would be based on an nginx image. The important part is that the nginx.conf is copied as a template into the image and not as a nginx config file:

FROM nginx:alpine
RUN rm -rf /usr/share/nginx/html/*
WORKDIR /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/templates/default.conf.template
COPY ./www .
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Running the example

Firstly, the docker image has to be built:

docker build -t ssi-example .

After the image was built, a corresponding container can be started. The docker -e flag is used to set the corresponding environment variables:

docker run --rm -it -p 9000:80 -e MY_NAME="world" -e MY_GREETING="hello" ssi-example

When opening the browser with the url http://localhost:9000 the output Hello World! should be displayed.

Conclusion

When having different environments (e.g., dev, stating, prod, ...) in which a web application is made available, different configuration parameters might be required. Further when the same docker image should be used for each environment, the proposed approach could be used to set corresponding configurations for each environment. SSI could even be used to print variables into other file types like javascript, css or similar.