6

I would like to proxy_pass to the related service conditionally based on environment variable.. What I mean, prox_pass adress should be change based on NODE_ENV variable..

What is the best approach of doing this ? Can I use if statement like as below for proxy_pass? If yes how should I do this ? Apart from this, I tried to create a bash as below as below to pass environment variable to nginx but could not able to set and pass $NGINX_BACKEND_ADDRESS to nginx conf somehow. Any help will be appreciated

   if ($NODE_ENV == "development) {
       proxy_pass http://myservice-dev;
   }

nginx.conf

server {
    listen  3000;
    location / {
        root /usr/src/app;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    location /csrf/token {
        proxy_pass ${NGINX_BACKEND_ADDRESS}/csrf/token;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    location /export/apis {
        proxy_pass ${NGINX_BACKEND_ADDRESS}/export/apis;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

entrypoint.sh

#!/usr/bin/env sh
set -eu
export NODE_ENV=development
if ["$NODE_ENV" == "development"]
then
    export NGINX_BACKEND_ADDRESS=http://backend-dev
elif ["$NODE_ENV" == "stage"]
then
    export NGINX_BACKEND_ADDRESS=http://backend-stage
elif ["$NODE_ENV" == "development"
then
    export NGINX_BACKEND_ADDRESS=http://backend-preprod
elif ["$NODE_ENV" == "development"]
then
    export NGINX_BACKEND_ADDRESS=http://backend
else 
    echo "Error in reading environment variable in nginx-conf.sh."
fi
echo "Will proxy requests for  to ${NGINX_BACKEND_ADDRESS}*"
exec /nginx-conf.sh "$@"

Dockerfile

FROM nginx:alpine AS production-build
WORKDIR /usr/src/app
ARG NODE_ENVIRONMENT=development
ENV NODE_ENV=$NODE_ENVIRONMENT
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/nginx.conf.template /etc/nginx/conf.d/default.conf.template
COPY nginx-conf.sh /
RUN chgrp -R root /var/cache/nginx /var/run /var/log/nginx /var/run/nginx.pid && \
    chmod -R 775 /var/cache/nginx /var/run /var/log/nginx /var/run/nginx.pid
USER nginx
COPY --from=builder /usr/src/app/dist .
ENTRYPOINT ["/nginx-conf.sh", $NODE_ENVIRONMENT]
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]

3 Answers 3

4

The Docker Hub nginx image (as of nginx:1.19) has a facility to do environment-variable replacement in configuration files:

[...] this image has a function, which will extract environment variables before nginx starts. [...] this function reads template files in /etc/nginx/templates/*.template and outputs the result of executing envsubst to /etc/nginx/conf.d.

So your first step is to rename your configuration file as is (including proxy_pass ${NGINX_BACKEND_ADDRESS}/...) to something like default.conf.template and put it in the required directory.

I would directly pass that address in your deploy-time configuration. I would not include it in the image in any way. (Imagine setups like "a developer is trying to run this stack on their local desktop system" where none of the URLs in the entrypoint script are right.) That also lets you get rid of pretty much all the code here; you would just have

# Dockerfile
FROM ... AS builder
...
FROM nginx:1.21-alpine
COPY nginx/nginx.conf.template /etc/nginx/conf.d/default.conf.template
COPY --from=builder /usr/src/app/dist /usr/share/nginx/html
# Permissions, filesystem layout, _etc._ are fine in the base image
# Use the base image's ENTRYPOINT/CMD
# docker-compose.yml
version: '3.8'
services:
  proxy:
    build: .
    ports: ['8000:80']
    environment:
      - NGINX_BACKEND_ADDRESS=https://backend-prod.example.com

If you are in fact using Compose, you can use multiple docker-compose.yml files to provide settings for specific environments.

# docker-compose.local.yml
# Run the backend service locally too in development mode
version: '3.8'
services:
  backend: # not in docker-compose.yml
    build: backend
    # and other settings as required
  nginx: # overrides docker-compose.yml settings
    environment:
      - NGINX_BACKEND_ADDRESS=http://backend
    # no other settings
docker-compose -f docker-compose.yml -f docker-compose.local.yml up
Sign up to request clarification or add additional context in comments.

Comments

1

if you want to run an if statement in your dockerfile, then you can use the RUN command in the dockerfile, for example using bash, RUN if [[ -z "$arg" ]] ; then echo Argument not provided ; else echo Argument is $arg ; fi

Comments

1

The way i normally do this is I have a generic nginx proxy and i then just pass in the url and protocol as env vars

ubuntu@vps-f116ed9f:/opt/docker_projects/docker_examples/load_balancer$ cat proxy.conf
server {
  listen 80 default_server;

  resolver 127.0.0.11 valid=1s;

  set $protocol $PROXY_PROTOCOL;
  set $upstream $PROXY_UPSTREAM;

  location / {
    proxy_pass $protocol://$upstream$request_uri;

    proxy_pass_header Authorization;

    proxy_http_version 1.1;
    proxy_ssl_server_name on;
    proxy_set_header Host $upstream;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection "";

    proxy_buffering off;
    proxy_read_timeout 5s;
    proxy_redirect off;
    proxy_ssl_verify off;
    client_max_body_size 0;
  }
}
ubuntu@vps-f116ed9f:/opt/docker_projects/docker_examples/load_balancer$ cat Dockerfile
FROM nginx:1.13.8

ENV PROXY_PROTOCOL=http PROXY_UPSTREAM=example.com

COPY proxy.conf /etc/nginx/conf.d/default.template
COPY start.sh /

CMD ["/start.sh"]

I then have a start script that will substitue the env vars into my proxy_config.

ubuntu@vps-f116ed9f:/opt/docker_projects/docker_examples/load_balancer$ cat start.sh
#!/usr/bin/env bash
envsubst '$PROXY_PROTOCOL,$PROXY_UPSTREAM' < /etc/nginx/conf.d/default.template > /etc/nginx/conf.d/default.conf

exec nginx -g 'daemon off;'

4 Comments

the issue is my side PROXY_UPSTREAM is dynamic..For instance, for development build it is example.com-dev whereas for stage it is example.com-stage.. Is there anyway to set this dynamically without passing extra arg for docker?
But couldnt you just add that logic in the start script. So you can set the PROXY_UPSTREAM in the start script rather than from the enviroment and then use envsubst to put that in the nginx config
But i would say from a build point of view since you must be setting NODE_ENV as part of the build environment you would be better to also set the url in the enviroment variables too. That way its easy to spin up a new container for a new env and url without having to change your start script and impact any other builds
This didn't work for me. I found a similar solution here that worked perfectly. You just put your conf file in /etc/nginx/templates/*.conf.template, the nginx image performs the env substitution, then copies the file to /etc/nginx/conf.d/*.conf.

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.