Search code examples
dockernginx

Serve two apps with the same internal ports on Docker network using nginx


I am serving two React apps (let's call them A and B) using Docker and I'm using NGINX to have a reverse proxy in order too make it easier to access them. All 3 containers are in the same network.

App A is being served on port 3000 and app B is server on port 3001

Here it is my default.conf file:

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

   location /app-a {
      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_pass http://app-a:3000;
   }

   location /app-b {
      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_pass http://app-b:3001;
   }
     
}

I can access both apps if I type their ports directly: my-ip:3000 and my-ip:3001

However, I can't access app B (returns 502) if I try to access by NGINX: my-ip:80/app-b. Note that my-ip:80/app-a works fine.

I noticed that this may be happening due to Docker's networking ports, even though they're exposed as 3000 and 3001 outside, they're both 3000 inside, so I can't access them properly by typing their containers name.

Currently, I'm serving app B by typing my ip directly:

location /app-b {
  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_pass http://my-ip:3001;
}

That's a cheap fix, but does not seem to be a good practice

One solution I tested was building the image for app B using different ports, but this a cheap fix

Any tips?


Solution

  • I'm going to use two simple Python serves in lieu of your React apps. Suppose that your project looks like this:

    ├── app-a
    │   ├── app.py
    │   ├── Dockerfile
    │   └── index.html
    ├── app-b
    │   ├── app.py
    │   ├── Dockerfile
    │   └── index.html
    ├── default.conf
    └── docker-compose.yml
    

    The contents of app-a/ and app-b/ would be replaced with your React applications.

    🗎 docker-compose.yml

    version: '3.7'
    
    services:
      app-a:
        container_name: app-a
        build:
          context: app-a
          dockerfile: Dockerfile
    
      app-b:
        container_name: app-b
        build:
          context: app-b
          dockerfile: Dockerfile
    
      nginx:
        container_name: nginx
        image: nginx:alpine
        ports:
          - "80:80"
        depends_on:
          - app-a
          - app-b
        volumes:
          - ./default.conf:/etc/nginx/conf.d/default.conf
    

    🗎 default.conf

    server {
        listen       80;
        listen  [::]:80;
        server_name  localhost;
    
       location /app-a {
          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_pass http://app-a:3000;
       }
    
       location /app-b {
          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_pass http://app-b:3000;
       }
    }
    

    To build and launch the Docker Compose stack:

    docker-compose build && docker-compose up
    

    You should now be able to access the two apps at http://127.0.0.1/app-a and http://127.0.0.1/app-b.

    enter image description here