Deploying ReactJS With Docker

How to package ReactJS with Docker and deploy it to Digital Ocean

Manny

For the purposes of this project, we’re going to use the standard Facebook Create React App as a base.

Assuming you have NodeJS version 10+ and can use the new npx feature, we’re going to scaffold out the project with:

npx create-react-app reactdocker;
cd reactdocker;

Test out the project:

npm start;

Should see it working on port 3000:

React Working On Port 3000

Stop the server with ctrl + c.

Next step is to create the ideal docker environment. Considering this is just HTML, CSS, and JavaScript, the only thing we need is an http server that can server up regular HTML. For this we’ll need NGINX. Starting fresh with alpine, let’s do this while in the repo directory:

docker run -it -p 3000:3000 -p 80:80 -v $PWD:/var/www/localhost/htdocs –name reactdocker alpine /bin/sh

While we’re in the container, let’s remove the node modules because they were installed with Mac OS and we need them to be installed with Alpine.

cd /var/www/localhost/htdocs;
# may take a few seconds
rm -rf node_modules;

We’ll need to install NodeJS and NPM for alpine:

apk add nodejs;
apk add npm;

Install the dependencies in the directory:

npm install;

Start the server:

npm start;# [Expected Output]# Compiled successfully!# You can now view reactdocker in the browser.# Local: http://localhost:3000/
# On Your Network: http://172.17.0.2:3000/# Note that the development build is not optimized.
# To create a production build, use npm run build.Same React Output But From Docker

Perfect, let’s stop the server and get it to build.

# press ctrl + c
npm run build;

We can see the files in the /build folder:

cd build;
ls -al;

Those files will need an http server to expose them on port 80, for this we’re going to add nginx:

apk add nginx;

Next we’ll need to configure the nginx configuration file to point to the build folder:

# add nano to edit the file
apk add nano;# modify the nginx default.conf file
nano /etc/nginx/conf.d/default.conf;

Here is the original file before:

File: default.conf

# This is a default site configuration which will simply return 404, preventing
# chance access to any other virtualhost.server {
listen 80 default_server;
listen [::]:80 default_server;# Everything is a 404
location / {
return 404;
}# You may need this to prevent return 404 recursion.
location = /404.html {
internal;
}
}

Here is the file modified:

File: default.conf

server {
listen 80 default_server;
listen [::]:80 default_server; location / {
root /var/www/localhost/htdocs/build; # this will make so all routes will lead to
# index.html so that react handles the routes
try_files $uri $uri/ /index.html;
}# You may need this to prevent return 404 recursion.
location = /404.html {
internal;
}
}

Create the directory to allow nginx to run on a process id:

mkdir /run/nginx;

Test the configuration file and start the server:

nginx -t;# [Expected Output]
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successfulnginx;

Now if we go to port 80 we can see build folder been served on nginx:

Nginx Serving React Build Folder

We can also see that the routing is working to point everything to react and give it responsibility to manage the routes:

Nginx Routing All Routes To React index.html

We’ve successfully created the environment so now we can create the Dockerfile to package things for deployment.

Now that we know what dependencies we need, we’ll create a Dockerfile and optimize if for deployment with the dependencies to do a build.

Before we destroy the container, let’s copy the nginx configuration file we modified to the repository folder itself. Press ctrl + p then ctrl + q to exit the container, then:

# create a new config directory
mkdir config;# copy default.conf to config/default.conf
docker cp reactdocker:/etc/nginx/conf.d/default.conf config/default.conf;# now we can destroy the container
docker rm -f reactdocker;

Let’s start with the base Dockerfile in root of the repository:

File: Dockerfile

FROM alpineEXPOSE 80ADD config/default.conf /etc/nginx/conf.d/default.confCOPY . /var/www/localhost/htdocsRUN apk add nginx &&
mkdir /run/nginx &&
apk add nodejs &&
apk add npm &&
cd /var/www/localhost/htdocs &&
rm -rf node_modules &&
npm install &&
npm run build;CMD [“/bin/sh”, “-c”, “exec nginx -g ‘daemon off;’;”]WORKDIR /var/www/localhost/htdocs

Let’s build it:

docker build . -t reactdocker

Running the container:

docker run -it -d -p 80:80 –name rdocker reactdocker;React running from Docker container image

Now our container is ready to be push to Docker Hub and ready to be deployed.

You’ll notice that the COPY takes a bit of time to complete, so we’ll add an ignore file and remove some of the dependencies that we no longer need.

File: .dockerignore

node_modules

Next we’ll modify the Dockerfile to remove the dependencies we don’t need:

File: Dockerfile

FROM alpineEXPOSE 80ADD config/default.conf /etc/nginx/conf.d/default.confCOPY . /var/www/localhost/htdocsRUN apk add nginx &&
mkdir /run/nginx &&
apk add nodejs &&
apk add npm &&
cd /var/www/localhost/htdocs &&
npm install &&
npm run build &&
apk del nodejs &&
apk del npm &&
mv /var/www/localhost/htdocs/build /var/www/localhost &&
cd /var/www/localhost/htdocs &&
rm -rf * &&
mv /var/www/localhost/build /var/www/localhost/htdocs;CMD [“/bin/sh”, “-c”, “exec nginx -g ‘daemon off;’;”]WORKDIR /var/www/localhost/htdocs

Let’s look at the size of the container before we build it again:

docker images | grep “reactdocker”;# [Expected Output]
# reactdocker latest 3c160b1a5941 16 minutes ago 489MB

Now we’ll build it again:

docker build . -t reactdocker;# and see the size difference
docker images | grep “reactdocker”;
# [Expected Output]
# reactdocker latest 669c991b23b6 20 seconds ago 36.6MB

That’s a huge difference from 489MB to 36.6MB.

Now run the container again:

docker run -it -d -p 80:80 –name rdocker reactdockerOptimized React Docker Container

Now that we have our optimized Docker image, let’s push it to Docker Hub.

docker tag reactdocker {docker-hub-username}/reactdocker;
docker push {docker-hub-username}/reactdocker;

After we’re successfully created our container, we’ll now go into Digital Ocean and deploy that same image.

Digital Ocean Create Droplet

Set some configuration for the Droplet:

Select pre-configured Docker One-Click appChoose a small size because this is just a demoChoose a region that is close to your for speedMake sure you’re ssh is set up correctly and create the droplet

Wait for things to complete and copy the IP address:

Copy Droplet IP address once complete

Now that we have the IP address, we can SSH into the container and perform the same docker run action:

# replace this droplet IP address with yours
ssh root@142.39.140.136;

It might prompt you for adding the key to the droplet, say yes.

Now that you’re in the droplet, let’s create that docker container:

docker run -it -d -p 80:80 –name rdocker {docker-hub-username}/reactdocker;

Once it’s up and running go the IP address in your browser, and you’ll see the react application is now running on the Digital Ocean droplet.

React running on Digital Ocean with Docker

We’ve now successfully packaged a ReactJS application in Docker and deployed it to Digital Ocean.

There are a few other things we could have done to improve things, including:

  1. Start tagging images with specific version to match a GitHub branch or tag
  2. Create a reverse proxy with a backend under the same url
  3. Create an SSL certificate to serve over HTTPS
  4. We could go a bit further into the nginx configuration with https://nginxconfig.io

I’ll create some more tutorials for those later on.

Any feedback or praise would greatly be appreciated.

Please share it on twitter or other social media platforms. Thanks again for reading.

Please also follow me on twitter: @codingwithmanny and instagram at @codingwithmanny.

How To Automate Docker Builds With GitHub

Create a NodeJS app pushed to a GitHub repository and have it automatically build on Docker Hub

medium.com

Deploy React To Firebase With CircleCI

How To Automatically Deploy React To Firebase Hosting With CircleCI

medium.com

Automating NodeJS TS Deployments With CodePipeline To Elastic Beanstalk

Set Up Continuous Deployments With AWS CodePipeline To Elastic Beanstalk Docker

medium.com

NodeJS Docker Deployment Process

How To Package NodeJS, Build & Deploy A Docker Container To Digital Ocean

medium.com


Leave a Comment

Your email address will not be published. Required fields are marked *