3

I have developed a react app via the includes live-server and deployed it. As a proxy server I am using nginx, which also serves some static and media files for the back- and frontend.

Especially to test the serving of static and media files I would like to have a dockerized local testing environment.

Here is my problem: In my test environment I do not want to create a new build every time I change the code (npm run build - for create-react-app). Ideally, I would have the ability to hot-reload via a bind mount.

This would mean that I would have to serve the development server via nginx. Is that at all possible? I am biting my nails over this problem for days, looking for a conventional way to do it.

Xen_mar
  • 8,330
  • 11
  • 51
  • 74

3 Answers3

4

It actually isn't too bad, just use an nginx config like this:

events {}

# assuming you want to serve your app on localhost:8080
# and assuming your webpack dev server runs on port 3000
http {
    include /etc/nginx/mime.types;
    server {
        # assuming you want to serve the app on localhost:8080
        listen 8080;
        client_max_body_size 50m;

        # webpack dev server
        location / {
            proxy_set_header X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header Host $host;
            # use your port for your webpack dev server
            proxy_pass http://host.docker.internal:3000/;
        }

        # this is specifically needed for hot reload with webpack dev server
        location /sockjs-node {
            proxy_set_header X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header Host $host;

            # 'host.docker.internal' is a docker dns record for your host machine's localhost,
            # and '3000' should be the port of your webpack dev server
            proxy_pass http://host.docker.internal:3000;

            proxy_redirect off;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
   }

    access_log /etc/nginx/access.log;
    error_log /etc/nginx/error.log debug;
}

And run nginx with a ./nginx.sh that looks like this:

#!/usr/bin/env bash

# path to your app's build directory
APP_BUILD_DIR=/path/to/my-app/build

# path of dir containing your nginx config
NGINX_CONF_DIR=$(pwd)

# name of your nginx conf file, relative to NGINX_CONF_DIR
NGINX_CONF_FILE=nginx.conf

# port at which nginx is serving your app
PORT=8080

# docker run docs: https://docs.docker.com/engine/reference/run/
# this will run an nginx container named 'nginx' as a daemon,
# and will mount NGINX_CONF_DIR in the container
docker run -d --name nginx \
  -v=$NGINX_CONF_DIR:/etc/nginx \
  -v=$APP_BUILD_DIR:/opt/base/my-app \
  -p=$PORT:$PORT nginx \
  nginx -c /etc/nginx/$NGINX_CONF_FILE -g "daemon off;"

And also a mime.types file in that directory that looks like this one.

So your directory structure should look like:

dir/
-- nginx.sh
-- nginx.conf
-- mime.types
-- ...

I'm assuming you're running your webpack dev server on your machine, not in a docker container? In my experience, I'd recommend that honestly. npm (or yarn) does its job so well that I haven't found a need to run my React apps in a container locally, even though I serve them through an nginx container.

Bonus: here's an nginx config if you want to run the same app, but serve the static bundle instead of the webpack dev server:

events {}

http {
    include /etc/nginx/mime.types;
    server {
        # assuming you want to serve the app on localhost:8080
        listen 8080;
        client_max_body_size 50m;

        location / {
            proxy_set_header X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header Host $host;
            root /opt/base/my-app;
            try_files $uri /index.html;
        }

        # matches things like http://localhost:8080/build/static/js/2.d63c51de.chunk.js
        location /build/static/ {
            alias /opt/base/my-app/static/;
            try_files $uri $uri/;
        }
   }

    access_log /etc/nginx/access.log;
    error_log /etc/nginx/error.log debug;
}

Reference: this SO post

Brendan Goggin
  • 2,061
  • 14
  • 14
  • 1
    Hey, thanks so much for taking the time! I will test this and accept the answer as soon as I have it working :) Thanks. Right now, I have a working solution by running the dev server in a container. The advantage here is, that I can use the docker dns and define the dev server as an upstream server in nginx. Are there advantages to no running the node server not in a container as you propose? – Xen_mar May 08 '19 at 07:56
  • I like building the frontend locally, outside of the container. It just seems easier. For a node server (other than webpack dev server) that needs to talk to other services (like a database), then I can see the advantages of running that using docker for its networking benefits. The beauty of docker is it's really up to you. But you'll notice the line that says `proxy_pass http://host.docker.internal:3000;`? that `host.docker.internal` bit is the docker dns record for your true `localhost`. I hope that clears some things up for you. – Brendan Goggin May 08 '19 at 16:10
1

Dockerizing development server with HMR is not a trivial task.

Even preconfigured projects (with HMR and docker-ready) probably won't work at the beggining without additional adjustments. F.e. HMR expects localhost path while you can have app working on another IP/port (CORS problems). Some adjustments can be hard - patching packages on the fly - during build processes.

Try to run ready project before trying to setup your dream config. You can always run different servers for different parts (on different ports).

I'd start with apollo-universal-starter-kit - it contains node.js api/backend parts but can work with external one (configurable).

You can search for other react-HMR-ready solutions on Docker Hub. Try, get inspirations and knowledge ... have fun.

xadm
  • 8,219
  • 3
  • 14
  • 25
0

For hot deploy to docker from local file system, specify the following options. Below is for docker running on windows 10 using powershell:

Set the environment variable
-e CHOKIDAR_USEPOLLING=true

Mount the volume of local machine into work directory in the container
-v ${pwd}:/<workdir in container>
 
E.g. : docker run -it --rm -p 5000:3000 -v /app/node_modules -v ${pwd}:/app -e CHOKIDAR_USEPOLLING=true <image id>
Prince Arora
  • 3,717
  • 1
  • 5
  • 5