loading...
Cover image for Run a React App in a Docker Container

Docker React Run a React App in a Docker Container

peterj profile image Peter Jausovec Updated on ・3 min read

Here are simple steps that show you how to start with an empty React app (using create-react-app), create a production build of that app and then run it inside a Docker container.

Let's start by creating a new React app:

  1. Install create-react-app
npm install create-react-app --global
  1. Create a new React app:
create-react-app react-docker-app
  1. Go to the react-docker-app folder and run it, to make sure all is good:
cd react-docker-app && yarn start

The yarn start command compiles the React app and opens the browser.

Now that we have the app running let's create a Dockerfile in the root folder of the project. Here are the contents of the Dockerfile:

Before we continue, let's explain what's happening in this Dockerfile.

Lines 1-4 are the first stage of the build. In this stage, you copy all source code to the container and execute yarn run build that creates an optimized production build.

Lines 6-10 are the second stage for the build. You install the serve package and on line 9, you copy the output from the first stage of the build from the folder /app/build to the current folder in the container (/app - this gets set by the WORKDIR /app instruction in the Dockerfile).

About multi-stage builds: If you're wondering about two FROM statements in the Dockerfile. This is because you want to use a multi-stage build. In the first stage of the build, you copy the source code to the container and run the build command. In the second build stage, you copy only the built artifacts (HTML, JS, ...) to the container. Using multi-stage build results in a significantly smaller Docker image. The first image in the example is ~198MB, while the second one is only 86.7 MB.

With the last line, you run the serve command to serve the contents of the current folder on port 80.

Instead of serve, you could also use Nginx; however that might require a bit more config.

To build the image, you can run the following command from the project root folder, where your Dockerfile is:

docker build -t react-docker-app .

With the -t you specify the name of the image, and with the . you specify the build context (e.g. the current folder). When the build completes, the last line should look something like this:

Successfully tagged react-docker-app:latest

Finally, let's run this container now. To run it locally, you need to provide the name of the image and the port we want the React app to be accessible on. Note that we used port 80 in the serve command, so need to make sure to use 80 when specifying the container port like this:

docker run -it -p 8080:80 react-docker-app

Once the container is running, you can open http://localhost:8080 and you'll be able to access the React app running inside the Docker container.

πŸ”₯ If you want to learn more about Kubernetes, Istio, Docker, and cloud-native in general, check out the my Learn Istio ebook πŸ“–. You can get a free preview of the book at πŸ‘‰ https://learnistio.com πŸ‘ˆ

Posted on Mar 5 '19 by:

peterj profile

Peter Jausovec

@peterj

Software engineer, author and international speaker. https://learncloudnative.com

Discussion

markdown guide
 

When trying to build I am getting this error

[1/4] Resolving packages...

error An unexpected error occurred: "registry.yarnpkg.com/react: getaddrinfo EAI_AGAIN registry.yarnpkg.com registry.yarnpkg.com:443".

 

Is your network connection ok? Are you running behind the proxy?

 

Hi there Peter!

I believe you made a mistake there at line #3 in the Dockerfile. You should copy files in project directory to your workdirectory which is /app. Now you declared COPY . . that is copying files to the same directory.

Cheers!

 

Hi Justina!

In the line above that one, I am setting the working directory (inside the container) to /app (WORKDIR /app) and then copying everything from build context root (which is the current folder on my host machine in this case: .) to the target folder inside the container. Since the working directory is set to /app the . in the COPY instruction copies everything to that folder.

If I removed the WORKDIR, I would also need to update the COPY instruction to read COPY . /app

Thanks,
Peter

 

Thanks for the article. I have followed along and all seems to build fine. However when I run the image and navigate to localhost:8080 I get a blank page with the text Error: Failed to fetch.

Any ideas on why this might be happening?

Also, what does the -s do in CMD ["serve", "-p", "80", "-s", "."]

 

Ignore the first part of my question. The Error: Failed to fetch is what my ReactJS app does when it can't mak calls to the API. :)

 

Great article, just faced one problem:

Step 4/9 : RUN yarn run build
 ---> Running in 09a2e8108274
yarn run v1.15.2
$ react-scripts build
/bin/sh: react-scripts: not found
error Command failed with exit code 127.

To solve this, I just added RUN npm install react-scripts -g --silent before the RUN yarn run build in the Dockerfile, and everything ran perfectly then on.

 

I had the same issue because I use don't use yarn. Solved by deleting node-modules, package-lock.json, and run the command yarn (this is similar to npm install). Then as he stated up above test the installation with yarn start.

 

Great article. Any reason for the 2 FROM statements? Thanks.

 

That's a great question! It has to do with the multi-stage build in Docker.

In the first FROM statement (first stage), you copy the source code to the container and run the build command (yarn run build). The build command generates everything that you need to run the app (JS, HTML, etc.)

In the second FROM statement (second stage of the build), on line 9, you copy the contents of the /app/build folder from the first container to the current one.

So the second image actually contains the built artifacts (HTML, JS, ...) and the serve package - it does not contain any of the node_modules or source code either.

Another advantage of using multi-stage builds is the size. The first image in the example is ~198MB, while the second one is only 86.7 MB.

Hope this explains it - I'll update the article to clarify this more as well.

 

Thanks very much fro the info.

 

Is it common to run the create-react-app command on the host machine or there's another way to do that (I mean inside the container)? We also have to consider that we could need some other dependencies inside our package.json. Do you know how to deal with that?

 

Usually, you would run create-react-app once, outside of the container on the host machine, just to create your app.

yarn build command takes care of the dependencies - on line 3 I am copying everything (package.json included) to the builder container, then yarn build does its magic and takes care of any dependencies that are needed.

Does that answer your questions?

 

Great article! Would be perfect if there was a mention of volumes so that we need not rebuild the image on each code change. Took me a while to find out about that, haha.

 

Thanks Amour! I guess it’s time for me to rewrite the article and add that :)

 

You forgot a health check, if you're using kubernetes - it's very important.

 

curious to hear how would that look like for a static website? If this was a backend service, I’d agree that you should probably have a /health or a similar endpoint; for static website, it doesn’t make much sense.

 

An HTTP health check would be good for Kubernetes, just simply visiting / to validate that serve is actually serving up pages, but that doesn't require adding a HEALTHCHECK line to your Dockerfile. Kubernetes ignores HEALTHCHECK statements and instead requires you to define this manually in your Pod/Deployment/... spec section. Your Dockerfile is fine as is for Kubernetes. If someone was going to deploy it directly via Docker, or use Swarm to orchestrate, then you'd want a HEALTHCHECK statement in your Dockerfile.

Aye, you can do that with wget.

 

Thanks a lot Peter, this helps me a lot. I just do some changes in your dockerfile to:

COPY . .

RUN yarn run build

RUN npm install
RUN npm run build

I'm not so sure why I need to do npm command twice, but there are too many discussion about these two differeneces, so I just drop it here in case somebody out there prefer to use npm rather than yarn for the build command

PS: Peter, I haven't found your article about dockerized Go project ;)

 

Ok, Thank you.
But where is the hot reload?

 

No hot reload as it's for deployment. I am not using Docker for development in this case :)

 

Please explain how can I run the code in a docker in the browser and debug it at the same time.
Given, I would like to send all react code through nginx proxy.

 

Amazing! Works like a charm! Thank you very much! Fast, easy, powerful and SO helpful! Thank you again! I'm using node 12, so I just changed 11 to 12 and of course the container name.

 
 

As I understand in both stages /app directory are not the same?

 

Yes, you're right. In the first stage, the /app folder contains all files due to the COPY . . command. In the second stage, the only thing in the /app folder are the contents of the /app/build folder from stage one (copy --from=builder /app/build .)