Skip to content

Instantly share code, notes, and snippets.

@xaviervia
Last active July 1, 2019 12:45
Show Gist options
  • Save xaviervia/6adea3ddba269cadb794 to your computer and use it in GitHub Desktop.
Save xaviervia/6adea3ddba269cadb794 to your computer and use it in GitHub Desktop.
Nginx and Docker links with environment variables, a love story

How to add environment variables to nginx.conf

This is the hack approach to adding environment variables to the nginx configuration files. As with most Google results for this search, the reason is Docker.

The setup

I intended to deploy two Docker containers.

  1. An instance of an HTTP server providing a REST service (in my case, a JBoss application server running a Spring application)
  2. An nginx that provides a proxy to the REST service.

Why would I want to use a proxy? Load balancing, caching, firewalling... in my case, it was that the original application did not provide CORS and the nginx configuration does. You can see the nginx configuration in the proxy.conf file.

The problem

Both the port and the IP address of the JBoss instance are assigned by Docker dynamically. Docker provides a linking feature for service discovery that allows Docker containers to know the IP and port of services running on a specific container, but the quirk is that it relies on environment variables.

I'm not actually certain that adding environment variables to an nginx configuration file is impossible, but its at the very least very hard.

The solution

So this is what I did: I created the base configuration file, named it proxy.conf and setup docker to add it to the conf.d directory while building the image. The command is:

ADD proxy.conf /etc/nginx/conf.d/proxy.conf

In the proxy.conf, I omitted the upstream configuration, leaving that for later. I created another file, a run.sh file and added it to the image using the Dockerfile. The file was as follows:

#!/bin/sh

(echo "upstream theservice { server $JBOSS_PORT_8080_TCP_ADDR:$JBOSS_PORT_8080_TCP_PORT; }" && cat /etc/nginx/conf.d/proxy.conf) > proxy.conf.new
mv proxy.conf.new /etc/nginx/conf.d/proxy.conf
service nginx start

What does is, it prepends the line with the upstream configuration.

Finally, I run the nginx from the run.sh script. The Dockerfile command:

CMD bash run.sh

The trick is that since the container is initialized like that, the configuration file does not get permanently written and the configuration is updated accordingly.

# This part is omitted
# upstream theservice {
# server 127.0.0.1:8082;
# }
server {
listen 80;
location / {
access_log off;
proxy_pass http://theservice;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET,POST,OPTIONS,PUT,DELETE,PATCH";
add_header Access-Control-Allow-Headers 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
add_header Access-Control-Allow-Credentials "true";
add_header 'Access-Control-Max-Age' 1728000;
add_header Content-Length 0;
add_header Content-Type text/plain;
add_header Cache-Control 'max-age=0';
return 204;
}
}
}
#!/bin/sh
# Preprend the upstream configuration
(echo "upstream theservice { server $JBOSS_PORT_8080_TCP_ADDR:$JBOSS_PORT_8080_TCP_PORT; }" && cat /etc/nginx/conf.d/proxy.conf) > proxy.conf.new
mv proxy.conf.new /etc/nginx/conf.d/proxy.conf
# Log the resulting configuration file
cat /etc/nginx/conf.d/proxy.conf
# Start nginx
service nginx start
@tsouza
Copy link

tsouza commented Mar 27, 2015

Hi,

I've tried your solution. Although I do see the correct configuration at log output, when I pull the proxy.conf from the container I see the original proxy.conf. Do you know what might be going on?

Thanks!

@bonifaido
Copy link

When you link containers together the /etc/hosts file gets updated with the linked container's entry, so you don't have to deal with environment variables at all.

@iDVB
Copy link

iDVB commented Mar 8, 2017

Works fine for docker-compose but need a solution for AWS EB Multicontainer which would have to use at method similar to this.

I've also seen it done where you use a bash tool like Sed to output the final file based on string replacement.

@stevencohn
Copy link

Here's another way, not necessarily better but it may be useful to some. First, I add a custom keyword to be replaced within the conf file. Second, create a dockerfile that declares an ENV variable and then a CMD that uses sed to edit the config before running nginx explicitly.

So suppose your default.conf contains this with the keyword "docker_host":
location /api { proxy_pass http://docker_host:9000/api; }

and write your Dockerfile similar to:

ENV docker_host localhost
ADD default.conf /etc/nginx/conf.d/default.conf
CMD sed -i.bak s/docker_host/$docker_host/g /etc/nginx/conf.d/default.conf && nginx -g "daemon off;"

then build the image and run the container using
docker run -d -p 80:80 -e "docker_host=${env:COMPUTERNAME}" imagename

@stguitar
Copy link

@stevencohn thats exactly what i was looking for!

@nervousapps
Copy link

nervousapps commented Jul 1, 2019

@stevencohn Works like a charm !!!!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment