392

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

root@87ede56e0b11:/# 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:

root@87ede56e0b11:/# 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:

root@87ede56e0b11:/# 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:

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

31 Answers31

197

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.

Omid Raha
  • 2,095
110

The official nginx image recommends using envsubst, but as pointed out by others it will replace also $host and other variables, which is not desirable. But fortunately envsubst can take as a parameter the names of variables to replace.

To avoid a very complex command parameter to the container (as in the linked example), you can write a Docker entrypoint script which will fill in the environment variables before executing the command. The entrypoint script is also a good place for validating the parameters and setting default values.

Here is an example of an nginx container which takes in API_HOST and API_PORT parameters as environment variables.

nginx-default.conf.template

resolver  127.0.0.11 valid=10s;  # recover from the backend's IP changing

server {
  listen  80;

  location / {
    root  /usr/share/nginx/html;
  }

  location /api {
    proxy_pass  http://${API_HOST}:${API_PORT};
    proxy_set_header  Host $http_host;
  }
}

docker-entrypoint.sh

#!/usr/bin/env sh
set -eu

envsubst '${API_HOST} ${API_PORT}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf

exec "$@"

Dockerfile

FROM nginx:1.15-alpine

COPY nginx-default.conf.template /etc/nginx/conf.d/default.conf.template

COPY docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
71

envsubstr is handled automatically by the nginx image now.

From the docs:

Out-of-the-box, nginx doesn't support environment variables inside most configuration blocks. But this image has a function, which will extract environment variables before nginx starts.

By default, this function reads template files in /etc/nginx/templates/*.template and outputs the result of executing envsubst to /etc/nginx/conf.d.

ESV
  • 155
xyhhx
  • 933
57

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/.

Tero Kilkanen
  • 38,887
43

Here another one with envsubst.

nginx docker image already runs envsubst through /docker-entrypoint.d/20-envsubst-on-templates.sh. You only need to place a template file in the right place: /etc/nginx/templates/my-file.conf.template (note: the file must have .template extension!).

Then, you can create a dedicated template file only for variables with one or more map clauses that we will use as our custom variables. You must choose a name for the conf.d file so that it is the first one that nginx takes.

This is the basic concept explained with docker-compose and three files:

docker-compose.yml:

version: "3.9"
services:
  nginx:
    image: nginx:1.23
    volumes:
        - ./template-variables:/etc/nginx/templates/10-variables.conf.template:ro
environment:
    EXTERNAL_IP: &quot;1.2.3.4&quot;

template-variables:

map $host $external_ip {
  default "$EXTERNAL_IP";
}

nginx.conf:

server {
     location / {
      proxy_set_header X-Real-IP $external_ip;
  }

}

izogfif
  • 103
David
  • 546
15

I wrote something that may or may not be helpful: https://github.com/yawn/envplate

It inline edits configuration files with ${key} references to environment variables, optionally creating backups / logging what it does. It's written in Go and the resulting static binary can be simply downloaded from the release tab for Linux and MacOS.

It can also exec() processes, substitute defaults, logs and has sensible failure semantics.

yawn
  • 251
6

What I did was to use the erb!

cat nginx.conf  | grep -i error_log

error_log <%= ENV["APP_ROOT"] %>/nginx/logs/error.log;

--After using erb

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

error_log /tmp/nginx/logs/error.log;

This is used in the Cloudfoundry staticfile-buildpack

Sample nginx configuration: https://github.com/cloudfoundry/staticfile-buildpack/blob/master/conf/nginx.conf

In your case

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env APP_WEB_1_PORT_5000_TCP_ADDR;
upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

become

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env <%= ENV["APP_WEB_1_PORT_5000_TCP_ADDR"] %>
upstream gunicorn {
    server <%= ENV["APP_HOST_NAME"] %>:<%= ENV["APP_HOST_PORT"] %>
}

#After applying erb

export APP_WEB_1_PORT_5000_TCP_ADDR=12.12.12.12
export APP_HOST_NAME=test
export APP_HOST_PORT=7089 

erb /etc/nginx/nginx.conf

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env 12.12.12.12
upstream gunicorn {
    server test: 7089
}
Sabith K S
  • 61
  • 1
  • 3
6

Referring to the answer on using erb, it can be done as below.

Write the NGINX config file as an erb file containing the environment variable, and evaluate it using the erb command to a normal config file.

erb nginx.conf.erb > nginx.conf

Inside the server block of the nginx.conf.erb file there could be

listen <%= ENV["PORT"] %>;
6

I accomplish this using a shell script.

Here is the nginx template:

server {

    listen 80;
    server_name ___MY_DOMAIN_NAME___;
    charset utf-8;

    location /proxy {
        proxy_pass http://___PROXY_IP___:___PROXY_PORT___;
        proxy_set_header Host            $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }

    location / {
        return         200;
    }

}

And the script to replace the environment variables is here:

echo sleep 3
sleep 3

echo build starting nginx config


echo replacing ___MY_DOMAIN_NAME___/$MY_DOMAIN_NAME
echo replacing ___PROXY_IP___/$LETSENCRYPT_IP
echo replacing ___PROXY_PORT___/$PROXY_PORT

sed -i "s/___MY_DOMAIN_NAME___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx.conf
sed -i "s/___PROXY_IP___/$PROXY_IP/g" /etc/nginx/nginx.conf
sed -i "s/___PROXY_PORT___/$PROXY_PORT/g" /etc/nginx/nginx.conf

cat /etc/nginx/nginx.conf

if [ -z "$MY_DOMAIN_NAME" ]; then
    echo "Need to set MY_DOMAIN_NAME"
    exit 1
fi  
if [ -z "$LETSENCRYPT_IP" ]; then
    echo "Need to set LETSENCRYPT_IP"
    exit 1
fi  
if [ -z "$LETSENCRYPT_PORT" ]; then
    echo "Need to set LETSENCRYPT_PORT"
    exit 1
fi
if [ -z "$LETSENCRYPT_HTTPS_IP" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_IP"
    exit 1
fi 
if [ -z "$LETSENCRYPT_HTTPS_PORT" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_PORT"
    exit 1
fi

nginx -g 'daemon off;'
Geige V
  • 161
  • 1
  • 3
5

There are many ways. Some have been outlined in some answers.

If you use the ngx_http_js_module, there is also a way to do that with JS:

## /etc/nginx/fetch_env.js
function fetch_upstream_host(r) {
  return process.env.UPSTREAM_HOST;
}

function fetch_upstream_port(r) {
  return process.env.UPSTREAM_PORT;
}

and

## /etc/nginx/nginx.conf
load_module modules/ngx_http_js_module.so;

env UPSTREAM_HOST;
env UPSTREAM_PORT;

http {
  js_include fetch_env.js;
  js_set $upstream_host fetch_upstream_host;
  js_set $upstream_port fetch_upstream_port;

  server {
    ...

    location / {
      ...

      proxy_pass http://$upstream_host:$upstream_port;
    }
  }

Please make sure you use njs module 0.33 or newer

Trung Lê
  • 151
4

nginx Docker image can extract environment variables before it starts, but it's a bit tricky. One solution is to:

  1. Add env variables to your nginx.conf file.
  2. Copy it to /etc/nginx/templates/nginx.conf.template in the container (as opposed to your normal /etc/nginx) in the build step or as a volume.
  3. Set the NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx environment variable in docker-compose.yml.

This will cause the nginx.conf.template file to be copied to /etc/nginx as nginx.conf and the environment variables will be replaced with their values.

There is one caveat to keep in mind: using command property in docker-compose.yml seems to be disabling the extraction functionality. If you need to run a custom command to start-up nginx, you can use the Dockerfile version.

I created a repo with the full setup, but in case it's not available:

# docker-compose.yml

version: "3" services: nginx-no-dockerfile: container_name: nginx-no-dockerfile image: nginx:1.23.1-alpine ports: - 8081:80 volumes: - ./site/index.html:/usr/share/nginx/html/index.html - ./site/nginx.conf:/etc/nginx/templates/nginx.conf.template working_dir: /usr/share/nginx/html environment: NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx API_URL: http://example.com

nginx-with-dockerfile: container_name: nginx-with-dockerfile build: context: ./site dockerfile: ./Dockerfile ports: - 8082:80 volumes: - ./site/index.html:/usr/share/nginx/html/index.html environment: NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx API_URL: http://example.com

# site/nginx.conf

worker_processes auto;

events {
}

http {
  include /etc/nginx/mime.types;

  server {
    listen 80;

    root /usr/share/nginx/html;
    index index.html index.htm;

    location / {
      try_files $uri $uri/ /index.html;
    }

    location /example {
      proxy_pass $API_URL;
    }
  }
}
# site/Dockerfile

FROM nginx:1.23.1-alpine

WORKDIR /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/templates/nginx.conf.template

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Run no Dockerfile version: docker compose up nginx-no-dockerfile

Run Dockerfile version:

docker compose build nginx-with-dockerfile
docker compose up nginx-with-dockerfile

Make sure to also have index.html file in the site folder.

filipkrw
  • 41
  • 2
4

Here's an example of using the sed approach, not necessarily better but it may be useful to some. First, 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
Thomas
  • 4,415
Steven
  • 41
3

Like already explained in Martin's answer, the official Docker image supports parsing templates with envsubst nowadays. However, envsubst does not support default values like a regular shell does, as in ${MY_VAR:-My Default}.

To circumvent that, one can use a custom entrypoint, like docker-defaults.sh:

#!/usr/bin/env sh
set -eu

As of version 1.19, the official Nginx Docker image supports templates with

variable substitution. But that uses envsubst, which does not allow for

defaults for missing variables. Here, first use the regular command shell

to set the defaults:

export PROXY_API_DEST=${PROXY_API_DEST:-http://host.docker.internal:8000/api/}

echo "Will proxy requests for /api/* to ${PROXY_API_DEST}*"

Next, let the original entry point do its work:

/docker-entrypoint.sh "$@"

Along with, say, some docker-nginx.conf:

# After variable substitution, this will replace /etc/nginx/conf.d/default.conf
server {
    listen 80;
    listen [::]:80;
    server_name localhost;
location / {
    root /usr/share/nginx/html;
    # Forward all non-existing paths, such as /help and /file/..., to the app
    try_files $uri $uri/ /index.html;
}

location /api/ {
   # Proxy API calls to another destination; the default for the variable is
   # set in docker-defaults.sh
   proxy_pass $PROXY_API_DEST;
}

# Redirect server error pages to the static page /50x.html
error_page 500 502 503 504  /50x.html;
location = /50x.html {
    root /usr/share/nginx/html;
}

}

In the Dockerfile copy it into /etc/nginx/templates/default.conf.template:

# Each time Nginx is started it will perform variable substition in the template
# file /etc/nginx/templates/default.conf.template and copy the result into
# /etc/nginx/conf.d/default.conf, so replacing the original configuration; see 
# also https://hub.docker.com/_/nginx
COPY docker-nginx.conf /etc/nginx/templates/default.conf.template
COPY docker-defaults.sh /

This will delegate to the original docker-entrypoint.sh

ENTRYPOINT ["/docker-defaults.sh"]

CMD ["nginx", "-g", "daemon off;"]

Now using, e.g., docker run --env PROXY_API_DEST=https://example.com/api/ ... will set a value, or in this example will default to http://host.docker.internal:8000/api/ if not set (which is actually http://localhost:8000/api/ on the local machine).

Arjan
  • 423
3

I know this is an old question, but just in case someone stumbles upon this (as I now have), there's a much better way to do this. Because docker inserts the linked container's alias in /etc/hosts, you can just do

upstream upstream_name {
    server docker_link_alias;
}

assuming your docker command is something like docker run --link othercontainer:docker_link_alias nginx_container.

cderwin
  • 131
2

Another option... I just found this tool today: https://github.com/kreuzwerker/envplate ... Written in Go, it can be installed very easily. Using it is quite simple. Though you will need to place template variables in your nginx.conf.

For example ${SOME_ENV_VAR} will be replaced when envplate's ep command is called on the file. So should your Dockerfile fail to get that binary or should it not run for some reason, it would lave your config invalid. Just a little note compared to other solutions like using perl or lua extensions.

I really like how you can set default values too for when the environment variable is not set. Ex. ${SOME_ENV_VAR:default-value} (and you can escape values). Again, envplate must still successfully run.

One benefit of using an approach like this is that you don't end up with a Docker image that's larger than necessary because you went off installing all sorts of extra modules you don't otherwise need. It may also be easier than using sed if things start to get complex and it contains that default value functionality.

Tom
  • 121
2

Another possibility is to use the 'sed' command with regular expressions, then you won't have to mess with your config files at all! That way you can use your config files normally, but when you run docker, it will swap the values out with the env variables. None of this "add a string of text to your config files that you search and replace with."

You can create a run.sh file with replacement values using your environment variables.

To change the "7s" on this line:

client_body_timeout 7s;         #Default 60s

Sed Command using client_body_timeout as search line and $client_body_timeout as replacement env variable:

sed -i "s/\(client_body_timeout\).*\?\;/\1 $client_body_timeout;/" /usr/local/nginx/conf/nginx.conf

Copy/paste this line for every parameter you want to set and change the client_body_timeout with the config option and $client_body_timeout with the env variable it is associated with. Use with existing config file and it will just work.

Jeff
  • 21
1

In addition to the lua and to the js method, there is a way with perl, especially, if nginx already compiled with --with-http_perl_module. Important parts are commented inline:

# declare variable
env ENDPOINT;

events { worker_connections 1024; }

http { include mime.types;

# store variable from env as nginx variable
perl_set $endpoint 'sub { return $ENV{&quot;ENDPOINT&quot;}; }';

server {
    listen 8080;
    root /usr/local/nginx;
    location / {
        sub_filter_once off;
        sub_filter_last_modified on;
        sub_filter_types application/javascript;

        # use variable, for example:
        sub_filter 'ENDPOINT' $endpoint;

        try_files $uri /testfile.js;
    }
}

}

Be aware, that perl is a dynamic module and probably you need libnginx-mod-http-perl or nginx-extras to be installed:

apt-get install libnginx-mod-http-perl
1

I think everything is simpler here. First of all, envsubst is better than lua block. envsubst creates configuration files before nginx starts and then all variables are static. When using a lua block, each time a request is made, the block will be processed and the environment variables will be initialized each time, and this is an additional burden. But this doesn't matter if you have a workstation. At high loads I think this will matter.

Further, if you go a little deeper into the details, you can simplify everything. Environment variables can simply be included in a separate file, which is generated from the template.

In container /docker-entrypoint.d/20-envsubst-on-templates.sh script handles the templates. All templates paths are saved in the created configuration files.
Example: /etc/nginx/templates/dir/my_vars.conf.template to /etc/nginx/dir/my_vars.conf

In *.conf.template templates you can include environment variables via the env directive - env VAR=${MY_VAR}.

Next, in any config file you include a variable file include /etc/nginx/dir/my_vars.conf

I'll explain with an example.

First, let's copy the nginx configs from the container

docker run -d --rm --name test nginx:latest && docker cp test:/etc/nginx/ $(pwd)/ngninx_config ; docker stop test
($(pwd)/ngninx_config directory must not exist)

Next, let's launch the container for debugging

docker run \
-e NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx \
-e MY_VAR=Hello \
--mount type=bind,src=$(pwd)/ngninx_config,dst=/etc/nginx \
--name test_nginx \
-it --entrypoint bash --rm nginx:latest

NGINX_ENVSUBST_OUTPUT_DIR variable see https://hub.docker.com/_/nginx => Using environment variables in nginx configuration (new in 1.19)

Сreate file /etc/nginx/templates/env/nginx.conf.template

env VAR=${MY_VAR}

Change file /etc/nginx/nginx.conf

user  www-data;
worker_processes  auto;
include /etc/nginx/env/nginx.conf; # <===

...

run command in container

/docker-entrypoint.d/20-envsubst-on-templates.sh && nginx -t

Debug your settings until you succeed.

The env directive is used in the main block. You can use the set directive. It is used in server, location, if blocks

(UPDATED)
Since setting environment variables in config files is a common difficulty (for example in mysql configs), I rewrote the /docker-entrypoint.d/20-envsubst-on-templates.sh file for general cases. The script in the templates replaces environment variables and creates config files.
https://gist.github.com/alexeyp0708/d244d0c6714d937bbcac55769c2bd392
See createFromTpl.sh -h command

1

Proper way to do this with lua, since the answer above is stale:

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

0

If you need to keep it simple: bash eval sed
And follow two simple rules: ${ENV_IN_PARENTHESIS} and always use ' quotes in template
You can swap all environment variables in one generic call.

 eval "echo \"$(sed 's/\$/##/g; s/\##{/${/g;' some.template)\"" | sed 's/\##/$/g' > some.conf


Whats happening:

A. $(sed 's/\$/##/g; s/\##{/${/g;' some.template)

Run the template through sed and:
1. replace all $ characters with ##
2. replace all ##{ back to ${

B. eval "echo \"$(sed 's/\$/##/g; s/\##{/${/g;' some.template)\""

Runs the modified template through eval which replaces visible ${ENV_IN_PARENTHESIS}

C. ... | sed 's/\##/$/g' > some.conf

Pipes the result of the eval to another sed call that returns all original $ back from ## and output the final result to your shiny new conf file.

0

Taking up @omid's solution, here's a way of doing it without custom scripting and/or changing the default entry point script of the official Nginx docker image:

in Dockerfile:

FROM nginx

override if necessary

ENV MY_VARIABLE='something'

do NOT override this

ENV DOLLAR_SIGN='$'

COPY ./nginx.conf.template /etc/nginx/templates/default.conf.template

In the local nginx.conf.template file:

upstream upstream_server {
 server ${MY_VARIABLE}:8001 fail_timeout=0;
}

server { ... location / { proxy_set_header Host ${DOLLAR_SIGN}http_host; .... } } }

0

Nginx 1.19 supports environment variables and templates in Docker

In this blog

ho raj
  • 1
0

ex:

set_by_lua $curr_domain_name 'return os.getenv("DOMAIN")';
add_header Content-Security-Policy 'script-src  ${curr_domain_name}';

This worked for me.

0

If you're not tied to bare installation of nginx, you could use docker for the job.
For example nginx4docker implements a bunch of basic env variables that can be set through docker and you don't have to fiddle around with nginx basic templating and all it's drawbacks.

nginx4docker could also be extended with your custom env variables. only mount a file that lists all your env variables to docker ... --mount $(pwd)/CUSTOM_ENV:/ENV ...

When the worst case happens and you can't switch/user docker, a workaround maybe to set all nginx variables with their names (e.g. host="$host") in this case envsubst replaces $host with $host.

Dave M
  • 4,494
0

The offical Nginx Docker image has a hidden hook Supporting this. See https://medium.com/@tal.potlog/nginx-docker-and-the-hidden-hooks-f5782261fae

Felix
  • 1
0

For future reference, this is our local setup:

docker-compose.yml

# docker-compose.yml

version: "3.8"

services:

nginx is used to handle SSL termination. The upstream app location

is the Django app, and it can be set to run in docker or on the

host. It is set using the NGINX_UPSTREAM_APP env var which is

injected into the app.conf using the app.conf.template file.

nginx: image: nginx ports: - "80:80" - "443:443" volumes: - ./docker/nginx/ssl:/etc/nginx/certs:ro - ./docker/nginx/templates:/etc/nginx/templates:ro environment: - NGINX_UPSTREAM_APP=${NGINX_UPSTREAM_APP:-django:5001}

app.conf.template

# Nginx host configuration for local development.
#
# The upstream app path is set dynamically when the nginx container is
# rebuilt, using a template and env var substitution.
#
# NB paths in this file are resolved inside the docker container.
upstream app {
    # host.docker.internal is the host machine
    server ${NGINX_UPSTREAM_APP} fail_timeout=0;
}

server { listen 80; listen 443 ssl; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!ADH:!MD5; ssl_prefer_server_ciphers on; ssl_certificate /etc/nginx/certs/localhost.dev.crt; ssl_certificate_key /etc/nginx/certs/localhost.dev.key; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_pass http://app; } access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; }

0
  1. Create a template directory that should contain the .conf files (make sure the files have .template extension e.g default.conf.template)

  2. mount the folder as a volume in the docker-compose file to /etc/nginx/templates (- ./templates:/etc/nginx/templates) ( see image below)

  3. Define the .env variables keys in your docker-compose file without the values ( see image below)

  4. run the docker compose up command and nginx will automatically create the conf files from the template directory before starting the engine.

Docker compose

Docker compose file

Default.conf.template

Default.conf.template

NB: No extra commands needed

0

According to @xyhhx and @ESV nginx can now handle ENV variables by creating a template, and placing it in directory defined in NGINX_ENVSUBST_TEMPLATE_DIR env variable.

TEMPLATE APPROACH - docker/nginx docs

However since approach with envsubst can still be a desired solution I've created easy approach that doesn't have drawback of having to modify variables in many places. So if someone wants to create their own custom solution with envsubst, here's a concise example.

docker-compose

  nginx:
    image: nginx:stable-alpine
    env_file:
      - ./config/nginx/.nginx-env
    entrypoint: /etc/nginx/setup_conf.sh

entrypoint

#!/bin/sh

LOAD ALL NGINX_* ENV VARS TO SHELL-FORMAT

envs=$(printenv | grep NGINX_ | awk -F '=' '{printf("$${%s} ", $1)}')

LOAD TEMPLATE, INJECT ENV VARIABLES, OUTPUT CONF WITH INJECTED VARIABLES

envsubst "$envs" < /etc/nginx/template.conf > /etc/nginx/nginx.conf

START NGINX IN THE BACKGROUND

nginx -g "daemon off;"

env file

NGINX_API_PATH=example.com
0

I will further expand David reply with a more complex example:

NGINX image do already have variable substitution; you can do it yourself with an envsub in your entrypoint (like i did before) or CMD but i do prefer using what it's already available.

I usually have an image that creates the right user and group (i usually run all my containers directly on 1000:1000 for security, with NGINX it's better to run the image as root to do variable replacing in config and make it read only for the user running NGINX) and various configurations with predefined config (ex. wordpress, laravel ,etc.) that i switch by changing the TEMPLATE env variable (ex. i set TEMPLATE="laravel" for laravel).

With the default NGINX image all the files ending with .conf.template under /etc/nginx/templates will be processed replacing variables and copied under /etc/nginx/conf.d/.conf; this works also with folders so /etc/nginx/templates/my-template.conf.template will generate /etc/nginx/conf.d/my-template.conf and /etc/nginx/templates/my-folder/another-template.conf.template will generate /etc/nginx/conf.d/my-folder/another-template.conf.

Let's start making a sample docker-compose file; this is just a sample to show where to mount the files and folders, you could also create a Dockerfile if you prefer

docker-compose.yml

version: "3.9"
services:
  nginx:
    image: nginx
    volumes:
        - ./templates:/etc/nginx/templates
        - ./nginx.conf:/etc/nginx/nginx.conf
environment:
    USER_NAME: &quot;user&quot;
    USER_GROUP: &quot;user&quot;
    TEMPLATE: &quot;my-template&quot;
    MAX_BODY_SIZE: &quot;1G&quot;
    PROXY_TIMEOUT: &quot;60&quot;
    MY_VAR: &quot;my-value&quot;

We then create a nginx.conf file that will import all the configuration files in conf.d

nginx.conf

include /etc/nginx/conf.d/*.conf;

We then create a default config file that in our case only import the correct template based on the TEMPLATE env; this could not be done on nginx.conf.

templates/default.conf.template

include /etc/nginx/conf.d/templates/$TEMPLATE.conf;

then we will create our templates under /templates/templates

templates/templates/my-template.conf.template

user $USER_NAME $USER_GROUP;
events {
}
http {
    client_max_body_size $MAX_BODY_SIZE;
    fastcgi_read_timeout $PROXY_TIMEOUT;
    proxy_read_timeout $PROXY_TIMEOUT;
    #....
}
Plokko
  • 101
  • 3
0

One more envsubst tested and works, no volume or messy hacks needed - extra cool!

FROM nginx:stable-alpine
RUN apk add jq curl
COPY default.conf /etc/nginx/conf.d/default.conf.template

COPY build /usr/share/nginx/html

CMD ["/bin/sh", "-c", "envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"]

See https://github.com/AAber/MultiEnvNginxReact for full action fun.

AAber
  • 161
-2

You should able to do what you want with Dockerize's templates.

Claude
  • 97