job.answiz.com
  • 4
Votes
name
name Punditsdkoslkdosdkoskdo

Can I use environment variables in Nginx.conf

[Cross-posted and edited down from https://stackoverflow.com/questions/21933955 as it was considered too sysadmin-like for StackOverflow.]

I have a docker container running Nginx, that links to another docker container. The host name and IP address of the second container is loaded into the Nginx container as environment variables on startup, but is not know before then (it's dynamic). I want my nginx.conf to use these values - e.g.

upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

How can I get environment variables into the Nginx configuration on startup?

EDIT 1

This is the entire file, after the suggested answer below:

env APP_WEB_1_PORT_5000_TCP_ADDR;
# Nginx host configuration for django_app

# Django app is served by Gunicorn, running under port 5000 (via Foreman)
upstream gunicorn {
    server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}

server {
    listen 80;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location /static/ {
        alias /app/static/;
    }
    location /media/ {
        alias /app/media/;
    }
    location / {
        proxy_pass http://gunicorn;
    }
}

Reloading nginx then errors:

$ nginx -s reload
nginx: [emerg] unknown directive "env" in /etc/nginx/sites-enabled/default:1

EDIT 2: more details

Current environment variables

[email protected]:/# env | grep APP_WEB_1
APP_WEB_1_NAME=/furious_turing/app_web_1
APP_WEB_1_PORT=tcp://172.17.0.63:5000
APP_WEB_1_PORT_5000_TCP=tcp://172.17.0.63:5000
APP_WEB_1_PORT_5000_TCP_PROTO=tcp
APP_WEB_1_PORT_5000_TCP_PORT=5000
APP_WEB_1_PORT_5000_TCP_ADDR=172.17.0.63

Root nginx.conf:

[email protected]:/# head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env APP_WEB_1_PORT_5000_TCP_ADDR;

Site nginx configuration:

[email protected]:/# head /etc/nginx/sites-available/default
# Django app is served by Gunicorn, running under port 5000 (via Foreman)
upstream gunicorn {
    server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}

server {
    listen 80;

Reload nginx configuration:

[email protected]:/# nginx -s reload
nginx: [emerg] directive "server" is not terminated by ";" in /etc/nginx/sites-enabled/default:3

 

From the official Nginx docker file:

Using environment variables in nginx configuration:

Out-of-the-box, Nginx doesn't support using environment variables inside most configuration blocks.

But envsubst may be used as a workaround if you need to generate your nginx configuration dynamically before nginx starts.

Here is an example using docker-compose.yml:

image: nginx
volumes:
 - ./mysite.template:/etc/nginx/conf.d/mysite.template
ports:
 - "8080:80"
environment:
 - NGINX_HOST=foobar.com
 - NGINX_PORT=80
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" 

The mysite.template file may then contain variable references like this :

listen ${NGINX_PORT};

Update:

But you know this caused to its Nginx variables like this:

proxy_set_header        X-Forwarded-Host $host;

damaged to:

proxy_set_header        X-Forwarded-Host ;

So, to prevent that, i use this trick:

I have a script to run Nginx, that used on the docker-compose file as command option for Nginx server, i named it run_nginx.sh:

#!/usr/bin/env bash
export DOLLAR='$'
envsubst < nginx.conf.template > /etc/nginx/nginx.conf
nginx -g "daemon off;"

And because of defined new DOLLAR variable on run_nginx.sh script, now content of my nginx.conf.template file for Nginx itself variable is like this:

proxy_set_header        X-Forwarded-Host ${DOLLAR}host;

And for my defined variable is like this:

server_name  ${WEB_DOMAIN} www.${WEB_DOMAIN};

Also here, there is my real use case for that.

  • 1
Reply Report

Doing this with Lua is substantially easier than it sounds:

server {
    set_by_lua $server_name 'return os.getenv("NGINX_SERVERNAME")';
}

I found that here:

https://docs.apitools.com/blog/2014/07/02/using-environment-variables-in-nginx-conf.html

Edit:

Apparently this requires installing the lua module: https://github.com/openresty/lua-nginx-module

Edit 2:

Note that with this approach you have to define env variable in Nginx:

env ENVIRONMENT_VARIABLE_NAME

You have to do this in toplevel context in nginx.conf or it won't work! Not in server block or in config of some site in /etc/nginx/sites-available, because it is included by nginx.conf in http context (which is not top-level context).

Also note that with this approach if you try to make a redirect e.g.:

server {
    listen 80;
    server_name $server_name;
    return 301 https://$server_name$request_uri;
}

it won't work as well:

2016/08/30 14:49:35 [emerg] 1#0: the duplicate "server_name" variable in /etc/nginx/sites-enabled/default:8

And if you give a separate variable name to it:

set_by_lua $server_name_from_env 'return os.getenv("NGINX_SERVERNAME")';

server {
    listen 80;
    server_name $server_name_from_env;
    return 301 https://$server_name$request_uri;
}

nginx won't interpret it and will redirect you to https://%24server_name_from_env/.

  • 1
Reply Report

If you're using a link, docker sets up environment variables and adds an alias for the linked container in /etc/hosts. If you are able to hard code the port (or if it's just port 80) you can simply do:

upstream gunicorn {
    server linked-hostname:5000;
}

The port is only available in an environment variable, which cannot be used in the upstream module, nor in server or location blocks. They can only be referenced in the main config, which doesn't help you. You could do this with the openresty bundle that includes Lua.

If you don't want to use openresty/Lua, another option is to do some substitution at container startup. Your docker run command could create the link then run a wrapper script that performs the appropriate substitution:

#!/bin/bash
/usr/bin/sed -i "s/server<gunicorn_server_placeholder>/${APP_WEB_1_PORT_5000_TCP_ADDR}/" default
start nginx
  • 1
Reply Report