5

For production, I have a Dockerfile which serves a React app using Nginx:

# Stage 1

FROM node:15.6.0-alpine3.10 as react-build
WORKDIR /app/client/
COPY package*.json ./
RUN npm install
COPY ./ ./
RUN npm run build

# Stage 2 - the production environment

FROM nginx:1.19.6
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=react-build /app/client/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

While for the backend written in Node / Express, I have the following Dockerfile:

FROM node:15.6.0-alpine3.10
WORKDIR /app/server/
COPY package*.json ./
RUN npm install
COPY ./ ./
EXPOSE 8080
CMD ["npm", "start"]

These containers are managed with this docker-compose.yml:

version: "3.0"

services:
  # React Client
  web:
    image: xxx.dkr.ecr.eu-west-2.amazonaws.com/client:latest
    ports:
      - "80:80"

  # Node Server
  server:
    image: xxx.dkr.ecr.xxx.amazonaws.com/server:latest
    command: npm start
    ports:
      - "8080:8080"

Here the nginx.conf:

server {
  listen 80;
  
  location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    try_files $uri $uri/ /index.html =404;
  }
  
  include /etc/nginx/extra-conf.d/*.conf;
}

PREMISES

  • On local everything works fine, I run React through react-scripts and the backend with docker-compose (and so without the React client)
  • Both images have been pushed to AWS ECR, their content is the equivalent of the Dockerfile above
  • When fetching the server, endpoints look like fetch("/users/:id", {..})
  • On package.json, I've set "proxy": "http://localhost:8080/"
  • Both images have both been tested and are working, both on dev and prod

PROBLEM

When hitting an api endpoint from the client, I get a 405 (Not Allowed).

That's actually expected, as I'm not really telling the client (Nginx) where to redirect these calls to.

Inspecting the network tab I can see the request is made against xxx.xxx.xxx.xxx:80 (which represents the client), when it should be redirected to same address but port 8080 instead (where the Express server stands).

On development it works since there's proxy set on package.json, but that's for development only, so it won't affect production.

WHAT I TRIED

  • Using links on docker-compose, not supported from AWS
  • Using driver networks on docker-compose, not supported from AWS
  • Adding proxy_pass on nginx.conf, but haven't been able to make it working

CONCLUSIONS

So premised all this, how can I connect a React build served with Nginx (client) to a Node server when both dockerized and on production?

I believe it should need some configuration on nginx.conf, but what I tried didn't land me that far.

Thank you in advance for your help!

9
  • Where you run the docker-compose ? In your local machine or on AWS ? Commented Jan 31, 2021 at 16:12
  • Mainly on AWS for production - It works if tested locally, but for development I wouldn't use it Commented Jan 31, 2021 at 16:21
  • where is your react code that call the backend? are you sure the requests are not https (thus, redirected) Commented Jan 31, 2021 at 18:20
  • The react code calling the backend is a simple fetch looking like fetch("/users", {..}) Can confirm the requests are not on https as the console error 405 (Not Allowed) contains the url on http - If you are curious about the react code itself, you can find the open source codebase here: github.com/ale917k/react-auth-starter Commented Jan 31, 2021 at 19:08
  • no need to delete your question even if it's a duplicate. It can be helpful for people searching for the same thing plus it's not a common question so we don't have a lot of them around. Commented Feb 2, 2021 at 15:09

2 Answers 2

2
+50

First you need to specify proxy pass directive for your api calls - I would propose to add /api in your fetch calls. Than provide upstream using the same name for your backend service as specified in docker-compose.yml. It is important that backend service proceed the web service in docker-compose.yml, otherwise you would get connection error in nginx like this nginx: [emerg] host not found in upstream "backend:8080" You can update your nginx.conf as follows:

upstream backend {
  server backend:8080;
}

server {
  listen 80;
  
  location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    try_files $uri $uri/ /index.html =404;
  }

  location /api {
    rewrite /api/(.*) /$1 break;
    proxy_pass http://backend;
  }
  
  include /etc/nginx/extra-conf.d/*.conf;
}

Or simply in your case provide a proxy pass to localhost as follows:

server {
   listen 80;
      
   location / {
     root /usr/share/nginx/html;
     index index.html index.htm;
     try_files $uri $uri/ /index.html =404;
   }
  
   location /api {
     rewrite /api/(.*) /$1 break;
     proxy_pass http://localhost:8080;
   }
      
   include /etc/nginx/extra-conf.d/*.conf;
}
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you for your answer, I think this is the right one, but for some reason when I try deploying the instances it seems to time out.. ibb.co/j5S3ZMk - Any idea why that's the case?
Unfortunately, I can't help you with AWS deployment. But you could try to build everything locally first, to ensure that everything is fine.
@ale917k I would also try to change service name from "server" to something else. It could probably cause some issues in nginx.conf.
Good point, done it and here's the outcome: Found out aws was not deploying as the nginx container was failing, returning nginx: [emerg] host not found in upstream "server:8080" in /etc/nginx/conf.d/default.conf:2 (and I've changed the server name to 'node' instead to avoid possible conflicts as suggested)
Tried changing upstream server:8080 to localhost:8080 to give it a shot and that solves the error, the container now runs (it gets deployed as well), but whenever hitting an endpoint it still doesn't work.. It returns a 502 (Bad Gateway) and the endpoint request seems to still be made against xxx.xxx.xxx.xxx:80
|
0

Sample Docker file, Included the Production build in the docker file

FROM node:15.6.0-alpine3.10 as react-build

# install and cache app dependencies
COPY package.json package-lock.json ./
RUN npm install && mkdir /react-frontend && mv ./node_modules ./react-frontend

WORKDIR /app/client/

COPY . .

RUN npm run build



# ------------------------------------------------------
# Production Build
# ------------------------------------------------------
FROM nginx:1.16.0-alpine
COPY --from=builder /react-frontend/build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

To add nginx as a server to our app we need to create a nginx.conf in the project root folder. Below is the sample file

server {

  listen 80;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}

1 Comment

How would this help redirecting frontend calls to the backend? You haven't set any proxy_pass, so everything that nginx.conf does is serving frontend files

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.