1

My project is using CI/CD for deployment and I have one docker-compose file for each application stage (dev, staging, release).

Depending on what stage the application is, I want to redirect the user for my API using Nginx for a different ip/port.

On my default.conf file I want to write something like this.

server {
    listen       443 ssl;

    ssl_certificate /etc/ssl/server/cert.pem;
    ssl_certificate_key /etc/ssl/server/privkey.pem;

    location / {
        proxy_pass https://api:$API_PORT;
        proxy_set_header Host $host;
        ...

where api is a reference for my service' IP that is defined in my docker-compose file and I want ${API_PORT} to be a reference to my environment variable that is defined inside docker-compose.

My docker-compose file looks like this.

version: "3"

services:
  api:
    ...
  ports:
      - 4000:4000
  nginx:
    ...
    environment:
      - API_PORT=4000
    ports:
      - 5180:80
      - 5181:443

How could I achieve that?

Note: If I have a static port, for example 4000, when I up both stage and release versions I will have conflicts on port 4000.

2 Answers 2

3

For accomplishing that you will need to set your Dockerfile and rename your .conf file in order to Nginx understand what you want to do.

First, Nginx by itself supports what you want to do, so you will need to use templates for that.

By default, if you place your config files inside /etc/nginx/templates and your filename ends with .template, Nginx will use envsubst to substitute your environment variable inside your .conf file for the values that you define in your docker-compose file.

So let's have an example.

You have default.conf.template (don't forget to rename your .conf files) file with your Nginx settings:

server {
    listen       443 ssl;

    ssl_certificate /etc/ssl/server/cert.pem;
    ssl_certificate_key /etc/ssl/server/privkey.pem;

    location / {
        proxy_pass https://api:$API_PORT;
        proxy_set_header Host $host;
        ...

Your Dockerfile will copy your default.conf.template file and will paste it inside /etc/nginx/templates

...
COPY /your/nginx/settings/folder/default.conf.template /etc/nginx/templates
...

With that done, when Nginx starts running it will search on the templates folder for *.template files, and when it finds your default.conf.template file it will replace the environment variables reference for the actual value and will move this file for /etc/nginx/conf.d folder.

So if your docker-compose file looks like this:

version: "3"

services:
  api:
    ...
  ports:
      - 4000:4000
  nginx:
    ...
    environment:
      - API_PORT=4000

your default.conf.template file (mentioned above) will be renamed to default.conf, moved to /etc/nginx/conf.d/ and will look like this:

location / {
      proxy_pass https://api:4000;
      ...

So Nginx will replace the references for the values and move the .conf files to the right place.

Sign up to request clarification or add additional context in comments.

Comments

1

In your Nginx configuration, you don't need to do anything; use the fixed port 4000.

proxy_pass https://api:4000;

Since this is a connection from the Nginx container to the API container, it stays within the Docker network environment. This connection doesn't pay any attention to what you might have set as ports:, it connects to the server process listening on port 4000 in the API container.

When you start the API container, the server process inside the container should use that same fixed port 4000. If you need to make the API container externally visible, you may choose a different number for the first port in the ports: block, but the second port needs to be 4000.

services:
  api:
    ports: ['4001:4000']
  nginx:
    ports: ['5180:80', '5181:443']

If you need to launch multiple copies of this stack, you need to change the first port number in all of the ports: blocks, but leave the second numbers unchanged.

If all access to the API container is through this Nginx proxy, you may not need the api: { ports: [] } block at all, and you can safely delete it; again, it's not used for connections between containers.

5 Comments

In my case all api access is made by Nginx, but the problem is that I am launching docker twice (staging and release) from the same codebase, so Nginx needs to know somehow what environment is being launched so it sets up correctly the proxy
The proxy_pass line is the same in both environments, and it targets the api container in the local Compose file (more specifically the Compose-created default network local to that file).
That is true, but as I have 2 api containers running, I need to specify into my proxy_pass what port the request is directed to, once that I can not have 2 containers sharing the same port.
Both api containers can listen on the same port 4000 and there won't be a conflict (internally, each has its own IP address). The host name api refers to a container on the current Docker network and correspondingly the current Compose file and host directory or $COMPOSE_PROJECT_NAME. The only difference between the environments will be what you publish out of Docker as ports:, but connections between containers don't require or consider that setting at all.
I finally tried what you said and it worked perfectly. Thank you so much!!

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.