6

This is my default.conf I'm replacing the original default.conf with the following one in my Dockerfile.

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

server {

    listen 443;
    server_name $servername;

    ssl_certificate           /etc/ssl/private/server.crt;
    ssl_certificate_key       /etc/ssl/private/server.key;

    ssl on;

    access_log            /var/log/nginx/ghost.access.log;

    location / {

      proxy_set_header        Host $host;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;

      proxy_pass          http://xxx:2368;
      proxy_read_timeout  90;

      proxy_redirect      http://xxx:2368 https://$servername:443;
    }
}

The error I receive is

2017/06/26 21:08:15 [emerg] 1#1: unknown "servername" variable
nginx: [emerg] unknown "servername" variable

When I replace $servername with 192.168.xx.xx (the real IP of my server), than everything works fine. But I want it to be dynamic.

How can I define the value of the $servername so that my docker will pick it up and it will be used by nginx?

Or can I write my default.conf in another way to make this dynamic?

2 Answers 2

10

Use envsubst to dynamically embed environment variables in the nginx configuration. envsubst is a lightweight template engine and it is also included in the official nginx:alpine image.

To install envsubst to your custom image:

alpine:

$ apk --no-cache add gettext

debian:

$ apt-get install gettext-base

Here is a simple example for how to use envsubst:

$ cat test.conf.template
hoge=$HOGE

$ docker run --rm \
    -v $(pwd)/test.conf.template:/tmp/test.conf.template \
    -e HOGE=aaa \
    nginx:alpine \
    /bin/sh -c "envsubst < /tmp/test.conf.template > /tmp/test.conf && cat /tmp/test.conf"
hoge=aaa

Note that if you want to use the $ symbol in the configuration file like nginx.conf, you need to specify the name of the environment variable to embed.

An example of dynamically embedding the environment variable SERVER_NAME in nginx.conf is as follows:

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

server {

    listen 443;
    server_name ${SERVER_NAME};

    ssl_certificate           /etc/ssl/private/server.crt;
    ssl_certificate_key       /etc/ssl/private/server.key;

    ssl on;

    access_log            /var/log/nginx/ghost.access.log;

    location / {

      proxy_set_header        Host $host;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;

      proxy_pass          http://xxx:2368;
      proxy_read_timeout  90;

      proxy_redirect      http://xxx:2368 https://${SERVER_NAME}:443;
    }
}

The arguments are somewhat complicated, so it is shown here in docker-compose.yml format:

version: '2'
services:
  nginx:
    image: nginx:alpine
    command: >
      /bin/sh -c
      "envsubst '
      $$SERVER_NAME
      '< /etc/nginx/nginx.conf.template
      > /etc/nginx/nginx.conf
      && nginx -g 'daemon off;'"
    volumes:
      - ./nginx.conf.template:/etc/nginx/nginx.conf.template
    ports:
      - 8080:80
    environment:
      SERVER_NAME: "test.example.com"
Sign up to request clarification or add additional context in comments.

2 Comments

Hi, thank you for this! I'm wondering how can I do this same thing but using multiple template files? For example, I have a ssl.conf file that I want to include in several server blocks so I clean the code a bit. How can I use env vars in that file as well? I think an extra envsubst call is needed, right? And what about moving everything to an entrypoint.sh file to clean the command a little bit? Thanks in advance
I think you can try below solution dcinja, it's better to handle the complex case, stackoverflow.com/a/65412923/6857874
0

Maybe you can try dcinja, it's my open-source project

dcinja use inja powerful library (like jinja) as template engine for run-time render template. It's support condition & include feature, that envsubst can't provided. Need to install dcinja in your docker image, and the binary is small.

here is the simple example

alpine:

$ apk --no-cache add wget libstdc++
$ wget https://github.com/Falldog/dcinja/releases/download/v1.3/dcinja-1.3.alpine.tar.gz \
    && tar xvzf dcinja-1.3.alpine.tar.gz
$ ./dcinja -j '{"SERVER_NAME": "www.test.com"}' -s /etc/nginx/nginx.conf.template -d /etc/nginx/nginx.conf

debian:

$ apt-get update && apt-get install -y wget
$ wget https://github.com/Falldog/dcinja/releases/download/v1.3/dcinja-1.3.linux-amd64.tar.gz \
    && tar xvzf dcinja-1.3.linux-amd64.tar.gz
$ ./dcinja -j '{"SERVER_NAME": "www.test.com"}' -s /etc/nginx/nginx.conf.template -d /etc/nginx/nginx.conf

Example

An example of dynamically embedding the environment variable SERVER_NAME in nginx.conf is as follows:

nginx.conf.template

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

server {

    listen 443;
    server_name {{ SERVER_NAME }};

    ssl_certificate           /etc/ssl/private/server.crt;
    ssl_certificate_key       /etc/ssl/private/server.key;

    ssl on;

    access_log            /var/log/nginx/ghost.access.log;

    location / {

      proxy_set_header        Host $host;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;

      proxy_pass          http://xxx:2368;
      proxy_read_timeout  90;

      proxy_redirect      http://xxx:2368 https://{{ SERVER_NAME }}:443;
    }
}

Dockerfile

need to build the image with dcinja library

FROM nginx:1.21-alpine
RUN apk add --no-cache wget libstdc++
RUN mkdir -p /app \
    && cd /app \
    && wget https://github.com/Falldog/dcinja/releases/download/v1.3/dcinja-1.3.alpine.tar.gz \
    && tar xvzf dcinja-1.3.alpine.tar.gz \
    && cp /app/dcinja /bin/

docker-compose.yml

You can assign the the env SERVER_NAME at docker-compose to control the runtime result of nginx.conf

version: '2'
services:
  nginx:
    build: .
    image: my-nginx:latest
    container_name: my-nginx
    command: >
        /bin/sh -c "
            dcinja --force-system-envs -e SERVER_NAME \
                -s /etc/nginx/nginx.conf.template \
                -d /etc/nginx/nginx.conf \
            && nginx -g 'daemon off;'
        "
    volumes:
      - ./nginx.conf.template:/etc/nginx/nginx.conf.template
    ports:
      - 8080:80
    environment:
      SERVER_NAME: "test.example.com"

Complex template example

You can maintain 3 template files, 1) nginx.conf.template 2) https.conf.template 3) http.conf.template. Determine the flag (enable_ssl) to include the right template at runtime.

{% if enable_ssl %}
    {% include "https.conf.template" %}
{% else %}
    {% include "http.conf.template" %}
{% endif %}

Other dcinja examples

input template from STDIN, output template to STDOUT

$ echo "TEST Name: {{ name }}" | dcinja -j '{"name": "Foo"}'
>>> TEST Name: Foo

input template from file, output template to file

$ dcinja -j '{"name": "Foo"}' -s input.template -d output.template

input json from file

$ dcinja -f param.json -s input.template -d output.template

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.