336

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
Hugo Rodger-Brown
  • 3,507
  • 2
  • 17
  • 12
  • 9
    This isn't a generic solution for environment variables, but if you're wanting to use environment variables for the hostnames/IP addresses of upstream servers, note that Docker (at least in recent versions) modifies /etc/hosts for you. See https://docs.docker.com/userguide/dockerlinks/ This means, if your linked container is called 'app_web_1', docker will create a line in /etc/hosts in your Nginx container. So you can just replace `server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;` with `server app_web_1:5000;` – mozz100 Jul 10 '14 at 17:18
  • 1
    Thanks @mozz100 - that's incredibly useful - /etc/hosts entries are much more effective than env vars in this case. The only bit missing is what happens if the upstream container is restarted, and acquires a new IP. I am presuming that the child containers will still point to the original IP, not the new one? – Hugo Rodger-Brown Jul 11 '14 at 13:04
  • 1
    Yes, if you restarted `app_web_1` it would get a new IP address, so you'd need to restart your nginx container too. Docker would restart it with an updated `/etc/hosts` so you wouldn't need to alter the nginx config file(s). – mozz100 Jul 14 '14 at 14:36
  • Reading through the answers here, I am flabbergasted that something so basic hasn't been elegantly implemented by probably the most popular web server on the planet. – csvan Nov 29 '20 at 14:46

23 Answers23

177

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
  • 1,895
  • 1
  • 10
  • 4
  • 4
    That kills nginx confs like `proxy_set_header Host $http_host;` – shredding Feb 17 '16 at 17:00
  • @shredding could you elaborate? why? – Philipp Kyeck Feb 18 '16 at 14:51
  • @shredding ok, just ran into the same problem :( did you find a way around this? I opened an issue: https://github.com/docker-library/docs/issues/496 – Philipp Kyeck Feb 18 '16 at 15:30
  • The problem is that envsubst thinks `$http_host` is an environment variable and replaces it with nothing. I haven't found a solution yet. – shredding Feb 18 '16 at 15:35
  • 35
    @shredding you can pass in variables names to be replaced - others are not touched: `command: /bin/bash -c "envsubst '$VAR1 $VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"` works for me b/c I know what they are called ... – Philipp Kyeck Feb 19 '16 at 10:13
  • 8
    ok, forgot to escape the `$`, should be `command: /bin/bash -c "envsubst '\$VAR1 \$VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"` – Philipp Kyeck Feb 19 '16 at 10:33
  • Is $VAR1 set in that context (e.g. within a `docker-compose.yml`)? – shredding Feb 19 '16 at 10:39
  • 2
    With consideration for escaping this works in your own Dockerfile: `CMD ["/bin/sh","-c", "if [ -n \"${SOME_ENV}\" ]; then echo envsubst '${SOME_ENV}' with ${SOME_ENV} && envsubst '${SOME_ENV}' < /etc/nginx/conf.d/default.template > /etc/nginx/conf.d/default.conf; fi ; nginx -g 'daemon off;'"]` – KCD Nov 28 '16 at 00:12
  • 2
    This is a great solution. Thanks. Also the link mentioned above https://github.com/docker-library/docs/issues/496 has a great solution for the issue. – kabirbaidhya Feb 09 '17 at 17:48
  • 5
    Escaping $ in docker-compose.yml should be done with another $. So, the command should look like this: `command: /bin/bash -c "envsubst '$${VAR1} $${VAR2}' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"` – sergei Jul 05 '19 at 10:49
  • I love @pkyeck solution. /bin/bash -c "envsubst '$VAR1 $VAR2' – Hlex Jul 29 '19 at 02:37
  • 1
    [As per my answer](https://serverfault.com/a/1022249/184199), the official `nginx` image can handle `envsubstr` for you – Martin Jun 20 '20 at 06:24
  • You actually have to copy template files to /etc/nginx/templates and then you don't need the command. That way your system variables won't be affected. – Logus Graphics Mar 06 '21 at 17:32
95

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;"]
Esko Luontola
  • 1,213
  • 1
  • 11
  • 9
  • 3
    I changed the script _slightly_ to clean up the template from conf.d. Final version: ``` #!/usr/bin/env sh set -eu envsubst '${API_HOST} ${API_PORT}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf rm /etc/nginx/conf.d/default.conf/template exec "$@" ``` great script though thanks! – phillipuniverse Nov 20 '19 at 18:29
54

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
  • 5
Martin
  • 743
  • 1
  • 6
  • 12
  • This is great! But, I don't see it happening. I put a `.template` file in `/etc/nginx/templates/`, but nothing is being written to `/etc/nginx/conf.d`. I am using the docker image `nginx:1.19.2`. I have a custom entrypoint script, but it ends up calling `nginx -g 'daemon off;'`. Am I missing something? – John L Aug 31 '20 at 20:31
  • 2
    @JohnL I didn't look thoroghly, but it seems as though this action is done via [a script included](https://github.com/nginxinc/docker-nginx/blob/master/stable/alpine/Dockerfile#L112) via the Dockerfile. You'll need to run that script to use that feature – Martin Sep 01 '20 at 04:36
  • 4
    Docs are a bit sparse. The missing steps that got this working for me were: 1) creating a template, `default.conf.template` in a `templates/` directory 2) in my `docker-compose.yml` adding `volumes: - ./templates:/etc/nginx/templates` to copy the templates. Then just do `docker-compose up` and et voila. – KhalilRavanna Feb 17 '21 at 19:18
  • Does this method require the official nginx docker image and all the files? I am trying this but its not working, My dockerfile is essentially "FROM nginx:1.19, EXPOSE 80, CMD ["nginx", "-g", "daemon off;"]" and my dir is just the dockerfile and a templates/default.conf.template and of course the docker-compose.yml filled from the official docs – Kent Wong Mar 16 '21 at 05:05
  • @KentWong refer to https://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf/1022249?noredirect=1#comment1342616_1022249 - there is a script included in the base image that must be run – Martin Mar 16 '21 at 18:21
  • 3
    As far as I can tell this only works if you have no `command`'s in your docker-compose. It seems those will prevent the `docker-entrypoint.sh` script from running. You can see that script if you exec into the running container. Posted about this [here](https://stackoverflow.com/questions/66732240/docker-compose-with-nginx-templates-to-pass-global-variables-adding-a-command-t). – Doug Fir Mar 21 '21 at 13:02
52

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
  • 34,499
  • 3
  • 38
  • 58
  • 3
    you may need `env NGINX_SERVERNAME` somewhere in your nginx.conf. – hiroshi Feb 15 '16 at 14:28
  • This did not work for me, although I have the lua module in my nginx docker image. Could this be related to the fact that I include a config file within my nginx.conf? I was trying to `set_by_lua` the variable in the included config file, while the `env MY_VAR` declaration was in the main nginx.conf, as suggested. What a shame, this would have been the cleanest solution! – jurgispods May 16 '17 at 10:16
  • Does anyone know the pros/cons to using this method of `envsubst`? I guess the pro is you don't need to run the `envsubstr` command before starting the server and the con is that you need to install the lua module? I wonder if there are any security implications on either approach? – Mark Winterbottom Mar 04 '18 at 16:19
  • @MarkWinterbottom didn't test this yet but it looks like you would not have to grant write access to the nginx config files, which you have to using `envsubst` and which is a no-go in my case – Gerrit-K Jun 05 '18 at 14:04
  • When you say the `env` directive has to be done "in toplevel context in nginx.conf", what exactly is "nginx.conf"? Where should that file be? – Jack M Oct 12 '19 at 20:23
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
  • 2
  • 3
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"] %>;
Ruifeng Ma
  • 181
  • 1
  • 5
  • 1
    Do I need to install Ruby then, inside the container? (If not, how is `erb` able to do variable substitution ... after the container was started, right?) — `erb` is this Ruby stuff right: http://www.stuartellis.name/articles/erb/ – KajMagnus Apr 08 '17 at 07:57
  • 1
    It's a command line tool that comes with standard Ruby installation. Try other options if you don't have it in the container. – Ruifeng Ma Apr 12 '17 at 11:45
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
  • 1
  • 1
  • A neat approach, however limited in that nginx variables can only be used in run-time directives, during the processing of each request; http://nginx.org/en/docs/faq/variables_in_config.html. For example, the njs approach won't work for most config directives such as listen, server_name, etc. – insertcoin Jun 10 '21 at 07:46
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,155
  • 5
  • 21
  • 28
Steven
  • 41
  • 1
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
  • 1
    You can get the host using this method but not the port. You have to either hard code the port or assume it's using port 80. – Ben Whaley Mar 18 '16 at 13:58
2

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
  • 403
  • 4
  • 8
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
  • 3
  • I advise https://github.com/gliderlabs/sigil as I ran into many bugs and problems with envplate . – Nick May 23 '18 at 17:49
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
  • 2
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{"ENDPOINT"}; }';

    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
Anton Bessonov
  • 111
  • 1
  • 3
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;
}

Eric Meadows
  • 111
  • 1
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.

Drone Brain
  • 111
  • 3
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
  • 1
    Hello! Please post complete answers instead of links to blog posts. Links can rot or be not a direct answer to the posted question. – ablackhat Nov 05 '21 at 05:26
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
  • 21
  • 30
  • 30
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
-2

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

Claude
  • 97