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 {

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


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

# 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

Root nginx.conf:

root@87ede56e0b11:/# head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

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
  • 8
    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 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? 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

19 Answers 19


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
 - ./mysite.template:/etc/nginx/conf.d/mysite.template
 - "8080:80"
 - NGINX_HOST=foobar.com
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};


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.

  • 3
    That kills nginx confs like proxy_set_header Host $http_host;
    – shredding
    Feb 17 '16 at 17:00
  • 33
    @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 ... Feb 19 '16 at 10:13
  • 7
    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;'" Feb 19 '16 at 10:33
  • 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 0:12
  • 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 5 '19 at 10:49

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.


resolver 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;


#!/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 "$@"


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;"]
  • 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! Nov 20 '19 at 18:29

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:



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:


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

  • 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! 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? Mar 4 '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
    – Griddo
    Jun 5 '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

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.

  • 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 via the Dockerfile. You'll need to run that script to use that feature
    – Martin
    Sep 1 '20 at 4:36
  • 2
    This should be the new accepted answer. Works like a charm.
    – Kolyunya
    Sep 18 '20 at 19:28
  • 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. Feb 17 '21 at 19:18
  • 2
    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.
    – Doug Fir
    Mar 21 '21 at 13:02

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.


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"] %>;
  • 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: stuartellis.name/articles/erb
    – KajMagnus
    Apr 8 '17 at 7: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

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
if [ -z "$LETSENCRYPT_IP" ]; then
    echo "Need to set LETSENCRYPT_IP"
    exit 1
if [ -z "$LETSENCRYPT_PORT" ]; then
    echo "Need to set LETSENCRYPT_PORT"
    exit 1
if [ -z "$LETSENCRYPT_HTTPS_IP" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_IP"
    exit 1
if [ -z "$LETSENCRYPT_HTTPS_PORT" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_PORT"
    exit 1

nginx -g 'daemon off;'
  • Works perfect with docker-compose. Jul 2 '20 at 8:21

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;
upstream gunicorn {


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=
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;
upstream gunicorn {
    server test: 7089

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;


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


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

  • A neat approach, however limited in that nginx variables can only be used in run-time directives, during the processing of each request; 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 7:46

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
  • It could be stated slightly clearer proxy_pass http://__DOCKER_HOST__:9000/api;
    – vatosarmat
    Nov 25 '21 at 12:01

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.

  • 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

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.


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.


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


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;


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.


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

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;

Nginx 1.19 supports environment variables and templates in Docker

In this blog

  • 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 5 '21 at 5:26

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

  • 8
    Can you add some more details? May 20 '16 at 11:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.