Home > Mobile >  How to host multiple Create-React-App production builds with NGINX, each in a separate directory (wi
How to host multiple Create-React-App production builds with NGINX, each in a separate directory (wi

Time:02-16

I'm trying to find a way how to host multiple (currently 2) React applications, each created with CRA. Both apps should run behind a single instance of NGINX and be accessible in a separate directory:

app1 -> http://localhost/app1

app2 -> http://localhost/app2

The 2 React applications where created like this:

npx create-react-app app1
npx create-react-app app2

To host both React applications using NGINX, the production build of each CRA app (npm run build) is copied to NGINX static directory /var/www/app[1|2]

# App1
npm run build
cp build/* /var/www/app1

# App2
npm run build
cp build/* /var/www/app2

This is how NGINX can be setup to host a single CRA app:

# /etc/nginx/conf.d/default.conf
server {
    listen 80;
    listen [::]:80;

    server_name localhost;

    location / {
        root /var/www/app1;
        index index.html;
    }
}

Now I'm trying to extend this example so both CRA apps are hosted by one NGINX.

Which modifications are necessary, both to NGINX site.conf and each React application itself?

I pushed a code showing my (incomplete) example above to Github for reference: https://github.com/wolkenarchitekt/multiple-cra-apps-behind-nginx. The code is using docker-compose for simplicity, but in the end the whole stack should run without Docker, so running separate Docker services for each React app is not an option.

CodePudding user response:

In order to run both React apps on the same domain but different subfolders, you should just need two location blocks:

root /var/www;
location /app1/ {
        index index.html;
    }
location /app2/ {
        index index.html;
    }

The necessary changes in the React apps usually include:

  • changing the homepage field in the package.json as described here
  • (only applies if react-router is used, older versions should work similarly) change the basename for BrowserRouterto your subdirectory: <BrowserRouter basename='/app1'>

EDIT:

After checking out your repo (before you pushed the new branch) I just got it working with the following Dockerfile (way worse for development since every code change requires a rebuild, I just like multistage builds and this would be more suited for a production environment):

FROM node:16 as builder1
WORKDIR /var/app
COPY app1/package.json .
COPY app1/package-lock.json .
RUN npm i
COPY app1/public/ ./public
COPY app1/src/ ./src
RUN npm run build

FROM node:16 as builder2
WORKDIR /var/app
COPY app2/package.json .
COPY app2/package-lock.json .
RUN npm i
COPY app2/public/ ./public
COPY app2/src/ ./src
RUN npm run build


FROM nginx:mainline-alpine
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./nginx/site.conf /etc/nginx/conf.d/default.conf
COPY --from=builder1 /var/app/build/ /var/www/app1
COPY --from=builder2 /var/app/build/ /var/www/app2

The following site.conf worked for me:

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

    server_name localhost;
    root /var/www;

    location /app1 {
        index index.html;
    }
    location /app2 {
        index index.html;
    }
}

I just had to change homepage to /app1 and /app2 respectively, change app2/App.js to actually output App2, and then

docker build . -t testreactmultiple:latest
docker run -it -p 3000:80 docker.io/library/testreactmultiple:latest  

I hope this helps you reproduce the working container that errors when the index is requested but works fine when /app1 or /app2 is requested.

  • Related