Docker-nginx: Using environment variables in nginx configuration not working

Created on 7 Jun 2020  路  7Comments  路  Source: nginxinc/docker-nginx

According to the documentation, I should be able to provide a template directory and the container will automatically extract and replace environment variables, dropping the resulting files in /etc/nginx/conf.d/. I don't see that behavior.

Base reproducible case

Given the following files (copied and pasted from the example in the documentation):

docker-compose.yml:

version: '3.7'

services:
  web:
    image: nginx
    volumes:
      - ./templates:/etc/nginx/templates
    ports:
      - "8080:80"
    environment:
      - NGINX_HOST=foobar.com
      - NGINX_PORT=80

templates/foobar.conf.template:

server {
  listen       ${NGINX_PORT};
}

I then run docker-compose up and attach a separate shell to my running web container.

From within the container I observe the following:

$ ls /etc/nginx/templates/
foobar.conf.template
# as expected

$ ls /etc/nginx/conf.d/
default.conf
# where is foobar.conf?

Thank you for your help and forgive me if I'm missing an obvious step!

Most helpful comment

I just tried and it works for me.

My docker-compose file :

version: '3.7'

services:

    nginx:
        container_name: nginx
        environment:
            NGINX_PORT: 80
        image: nginx
        restart: always
        volumes:
            - ./docker/nginx/templates:/etc/nginx/templates:rw,cached

And my default.conf.template file :

server {

    root /var/www/project/public;
    listen ${NGINX_PORT};

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    # optionally disable falling back to PHP script for the asset directories;
    # nginx will return a 404 error when files are not found instead of passing the
    # request to Symfony (improves performance but Symfony's 404 page is not displayed)
    # location /bundles {
    #     try_files $uri =404;
    # }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        # fastcgi_param APP_ENV prod;
        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # return 404 for all other php files not matching the front controller
    # this prevents access to other php files you don't want to be accessible.
    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

You need to create your file and to copy it in /etc/nginx/templates.
This file will not be changed by nginx, but the content will be replaced and you should see it in /etc/nginx/conf.d/default.conf.

You will have in your default.conf file :

server {

    root /var/www/project/public;
    listen 80;

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    # optionally disable falling back to PHP script for the asset directories;
    # nginx will return a 404 error when files are not found instead of passing the
    # request to Symfony (improves performance but Symfony's 404 page is not displayed)
    # location /bundles {
    #     try_files $uri =404;
    # }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        # fastcgi_param APP_ENV prod;
        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # return 404 for all other php files not matching the front controller
    # this prevents access to other php files you don't want to be accessible.
    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

I hope this helps 馃槈

All 7 comments

Same problem here. I also tried manually running nginx -g 'daemon off;' -T and it's using the original default.confonly.

Using nginx:1.19.0-alpine

I've just tried it and it works.

Can you "docker pull nginx" or "nginx:1.19.0-alpine" and then docker-compouse up -d and then show the output of docker logs for the launched container?

For my case, I know what the problem was.

I was overriding CMD in my Dockefile. By taking a look at the content of the entrypoint script of the Nginx docker image:

if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then

This means that, if your custom CMD does not start with nginx or nginx-debug (which was the case for me), none of the /docker-entrypoint.d scripts will be run because of how docker-entrypoint.sh works. The Nginx template compilation step is one of those scripts.

This is very unclear in the README of the image on dockerhub and I was only able to figure it out by looking at the code of the entrypoint script.

My solution was to simply add whatever I want to execute on entrypoint to a custom script and copy it to /docker-entrypoint.d/ so that it gets executed with the existing scripts, and just not overriding CMD or ENTRYPOINT in my custom image Dockerfile.

I think the README should be updated to reflect how this custom templating function actually works and how the entrypoint works too.

I also recommend taking a look at this how CMD and ENTRYPOINT interact.

I also recommend taking a look at this how CMD and ENTRYPOINT interact.

Is there something there that should be considered?

Unfortunately that table isn't correct/complete. Everything under the ENTRYPOINT exec_entry p1_entry should start with /bin/sh -c 'exec_entry p1_entry' (the quotes are important since it is a single argument for the -c of /bin/sh) and CMD needs to be shown as extra arguments to the sh -c. While these extra args wouldn't usually have an effect, they do become arguments to the "script":


entrypoint/cmd examples:

$ docker build .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM bash
 ---> 2437bbe54138
Step 2/3 : ENTRYPOINT echo "'self': $0"; i=0; for arg in "$@"; do echo "$i: $arg"; let "i=i+1"; done
 ---> Running in 3f01c9594b33
Removing intermediate container 3f01c9594b33
 ---> 0bd7f79cb7f3
Step 3/3 : CMD arg1 arg2
 ---> Running in f0b15fc9586e
Removing intermediate container f0b15fc9586e
 ---> 583dd978ce72
Successfully built 583dd978ce72
19:47:44 [sauron@isengard ~/tmp/entrypoint]$ docker run -it --rm 583dd978ce72
'self': /bin/sh
0: -c
1: arg1 arg2
$ docker build .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM bash
 ---> 2437bbe54138
Step 2/3 : ENTRYPOINT echo "'self': $0"; i=0; for arg in "$@"; do echo "$i: $arg"; let "i=i+1"; done
 ---> Using cache
 ---> 0bd7f79cb7f3
Step 3/3 : CMD ["arg1", "arg2"]
 ---> Running in 44b2751d10b8
Removing intermediate container 44b2751d10b8
 ---> 67ce95e48e75
Successfully built 67ce95e48e75
$ docker run -it --rm 67ce95e48e75
'self': arg1
0: arg2

Regardless, the entrypoint and command here are working as designed (and very similar to most other images in the official-images program, e.g. redis).

@TamerShlash :

I was overriding CMD in my Dockefile.

Same for me (but ENTRYPOINT). Removing ENTRYPOINT from child image resolved the issue..

I just tried and it works for me.

My docker-compose file :

version: '3.7'

services:

    nginx:
        container_name: nginx
        environment:
            NGINX_PORT: 80
        image: nginx
        restart: always
        volumes:
            - ./docker/nginx/templates:/etc/nginx/templates:rw,cached

And my default.conf.template file :

server {

    root /var/www/project/public;
    listen ${NGINX_PORT};

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    # optionally disable falling back to PHP script for the asset directories;
    # nginx will return a 404 error when files are not found instead of passing the
    # request to Symfony (improves performance but Symfony's 404 page is not displayed)
    # location /bundles {
    #     try_files $uri =404;
    # }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        # fastcgi_param APP_ENV prod;
        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # return 404 for all other php files not matching the front controller
    # this prevents access to other php files you don't want to be accessible.
    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

You need to create your file and to copy it in /etc/nginx/templates.
This file will not be changed by nginx, but the content will be replaced and you should see it in /etc/nginx/conf.d/default.conf.

You will have in your default.conf file :

server {

    root /var/www/project/public;
    listen 80;

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    # optionally disable falling back to PHP script for the asset directories;
    # nginx will return a 404 error when files are not found instead of passing the
    # request to Symfony (improves performance but Symfony's 404 page is not displayed)
    # location /bundles {
    #     try_files $uri =404;
    # }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        # fastcgi_param APP_ENV prod;
        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # return 404 for all other php files not matching the front controller
    # this prevents access to other php files you don't want to be accessible.
    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

I hope this helps 馃槈

same problem with 1.9.15-alpine.
@abdounikarim may be, that works for the default.conf template, but the issue here is that we want it to work for a custom template.

OK, finally that works for me with 1.9.15-alpine :

cseunion-nginx | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
cseunion-nginx | 20-envsubst-on-templates.sh: Running envsubst on /etc/nginx/templates/default.conf.template to /etc/nginx/conf.d/default.conf
cseunion-nginx | 20-envsubst-on-templates.sh: Running envsubst on /etc/nginx/templates/site.conf.template to /etc/nginx/conf.d/site.conf
cseunion-nginx | /docker-entrypoint.sh: Configuration complete; ready for start up

Both files gets created.
This issue should be considered resolved.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sandersaares picture sandersaares  路  6Comments

hasancansaral picture hasancansaral  路  6Comments

lucacome picture lucacome  路  3Comments

luckymark picture luckymark  路  4Comments

rokf picture rokf  路  5Comments