The advent of cloud-native computing has greatly transformed the landscape of building and distributing software. You have various tools to facilitate everything from integrating and packaging application code to deploying and scaling it. The practice of doing this is known as DevOps, and the concept that sits at the heart of modern-day DevOps is containers.
In this tutorial, you will learn to deploy a React app by creating a Docker image, pushing it to a Container Registry, and deploying it using a simple Droplet server.
docker
and npm
should be installed on your machine.Before proceeding, let’s walk through some basic terminology.
A container is “a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another.”
Imagine a container as a lightweight virtual machine running your application, and you can spin it up/down using a container image.
You cannot talk about containers without talking about container image, which is “a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries, and settings.”
A container image becomes a container at runtime.
Containers make it really easy to package, deploy, distribute, and scale application code. They encapsulate your code and all the dependencies required to run it in a neat, multi-platform, easily replicable bundle.
One of the most well-known and widely used ways of containerization is using Docker. You can read about it in this article.
Now, let’s dive into how you actually containerise your application.
You will start off by installing Docker. Here’s a pretty good guide to get it up and running. Once it is set up, check the docker
version to ensure it’s running fine.
Next, you spin up the React app. You should have your React app on your local machine or a GitHub repository. If you have it on Github, clone the repo to your machine OR create a new sample react app using Vite, by running the following command:
This should initialize a sample React app inside the react-app
directory. Let’s run the app to ensure it’s working properly.
Wait for your app to compile. Once it’s done, you should see a message similar to the following.
And you should be able to view your app in your browser at localhost:5173
.
Great, the app is up and running on your machine. Now, all that remains is to package it somehow and get it to production.
Docker helps you containerize and run your application code using Docker/container images. To build such an image you use something called Dockerfile
which contains instructions to build the image. This image consists of many read-only layers and each Dockerfile
instruction adds a new layer to the image. Each of these layers is stored as a SHA-256
hash. You can learn more about the Docker image constitution here.
So you will create a Dockerfile
to build your app image and it will consist of two important steps:
nginx
to serve the app.Let’s go over each one.
Note: In the next two steps, all the instructions will go inside the Dockerfile
.
You will ask Docker to use the latest Node.js image as a base to build your React app. It’s like setting up the foundation for building construction – you need a solid starting point.
Establish the working directory within the container to execute all subsequent commands. Think of it as defining the workspace where your project will come to life.
Now, you will copy your package.json
file into the container and run npm install
to install all the dependencies listed in the package.json
file. This step is crucial for ensuring that your React app has everything it needs to run smoothly.
Next, copy the rest of your project files into the container and run npm run build
to compile the app. This step transforms the source code into a production-ready bundle optimized for performance and efficiency and places it under the dist/
directory.
Nginx is a popular web server known for its speed and efficiency, making it ideal for serving your React app to the users. You will use the latest nginx
image as the base for your server.
Firstly, you will customize nginx
’s configuration by replacing its default settings with your own. This ensures that nginx
knows how to handle requests and serve the app correctly.
Then copy the compiled React app from the builder
container into the directory where nginx
expects to find the files it serves.
This step ensures that nginx
can access your app’s files and serve them to visitors.
Set the working directory within the nginx
container to where your React app files are located.
Lastly, start the nginx
server with the following command. It tells Docker to launch nginx
and keep it running.
Finally putting everything together, this is what your Dockerfile
will look like:
So create a new file called Dockerfile
in the root directory of your app:
And copy the contents of the Dockerfile
we created above inside this file.
You’re almost done. Just missing one crucial piece of the puzzle before you are ready to build the image. If you’re wondering where your nginx
config is, you’re right on the money. That’s the vital component you need to complete the picture.
To ensure step 2 works correctly, create a directory called nginx
at the root of your app and add an empty default.conf
file to the same directory.
Copy the following configuration to default.conf
:
This asks nginx
to start listening on port 80
, specifying the files to serve, and configuring some default error based on the status code. You can read this tutorial to understand the nginx
configuration file structure and configuration contexts. Also, here’s a really handy UI tool for constructing nginx
config files.
With that, you are all set to build and deploy your image.
Make sure you cd
back to the root dir of your app – where the Dockerfile
is and run the following:
An image is identified by its name and tag, which are specified by the —t
flag. So, the name of your image is react-app
, and the tag is v1.0
. Notice that there’s a --platform
flag to specify the architecture for which you build the image. Usually, you can omit this, but sometimes, while building cross-platform images, it’s required so that the image is compatible with the platform it’s intended to run on. You can build the same image for multiple platforms if required.
You should see something similar to the following once the build finishes:
Verify that your image is built successfully. Use the following command:
You should see your app in the list of images, as shown above.
Now that the image is ready, it’s time to prepare it for distribution and deployment. As mentioned earlier, the awesome part about an image is that it is self-sufficient and can be run on any platform that supports Docker.
Naturally, the image must be stored in a centralized location before it can be distributed and deployed.
That’s where a Container Registry comes into the picture.
Container Registry is a centralized repertoire of container images. It lets you store container images for rapid deployment. You can push your images to a Container Registry and then pull from anywhere – be it locally, from a Kubernetes cluster, CI/CD pipeline, etc. Let’s look at how to push the image to a Container Registry.
You must set up your Container Registry before you can start pushing images to it. You should use the private Container Registry by DigitalOcean. It is simple, private, and secure. You can choose from multiple tiers, and you can start by simply using the free tier, which offers up to 500MB of storage with a single repository—which should be more than enough to push your app image.
Setting up the DigitalOcean Container Registry is fairly simple and fast. You can follow these instructions to get started.
For this example, you can set up a free-tier registry in the BLR1
region and call it my-container-registry
.
Note: You need to have a DigitalOcean account to use Container Registry, so make sure to sign up if you haven’t already.
The next step is to get the API token to access the registry. You’ll need this later on. Go to the API Tokens page to generate the token.
Click on Generate New Token to see a form. Fill out the form details and select Full Access. Since you are the only one using this token, it shouldn’t really be a security concern.
An API token will be generated for you. Make sure to copy this token somewhere safe. Later, you will need it to log in to your registry.
Now let’s log in to the registry. Run the following in your terminal and enter the login credentials:
Now, you are ready to push the image. So, let’s tag it for pushing.
And push the image:
The image you built now sits inside the Container Registry and should appear on the cloud control panel.
As you can see, the image has been pushed to the registry and is tagged latest
. That’s because no tag name was specified while tagging the image for pushing. If no tag is specified, latest
is chosen as the default tag name.
You must note that you can tag your Docker image with any identifier you choose. One practical application of this feature is versioning your images.
For instance, let’s say you implement changes to your code and want to associate those changes with a specific release, such as v1.0
. By tagging your image with v1.0
and pushing it to your registry, you establish a clear reference point for that release. As you continue to update and release, you can incrementally increase the version numbers (e.g., v1.1
, v1.4
, v2.0
), providing a chronological record of your code releases within your registry. This systematic approach helps maintain a structured history of your app’s development and simplifies tracking and managing different versions over time.
You have built and pushed your image. Now, you only need to spin up a Droplet and deploy your container using the image.
Follow the guide here to spin up a droplet. The $6 variant should be good; set Ubuntu as the OS of choice. Once you have a Droplet running, you should see it in your control panel.
Click it open and copy the IP address of your droplet. As the arrow points out, you will find it right under the droplet’s name and details.
Using the IP address, you will now ssh
into the Droplet as root.
Based on the authentication method you chose while setting up the Droplet, you may or may not have to provide a password to ssh
into your Droplet.
Once you are successfully ssh
-ed into your Droplet, you should see a welcome message similar to the root prompt.
You’re now inside the droplet.
Once you’re inside the droplet, you must pull and run the app image from your registry.
Check if you have Docker on your Droplet:
If not, install Docker:
Once installed, log into the registry again following the same steps as stated earlier:
And pull the image:
This pulls the image with the latest
tag. Remember that you can specify the tag you want to pull. Since the tag name was omitted, docker pulled the latest
one by default.
Now you have your container image, which consists of the latest React app code with all of the dependencies required to run it. If you recall, a container image is what becomes a container at runtime. So, let’s run the container using the image you just pulled now.
The -d
flag detaches the container and runs it in the background. The -p 80:80
maps port 80
on the host to port 80
on the container, directing traffic to the container’s port.
Port 80
is exposed because it is the standard port for HTTP traffic and where nginx
would be listening for incoming requests.
Run the following to make sure your container is up:
You should see your container in the list. Check the status - it should say something like “UP 2 seconds”. That means your container is up and running.
Congratulations! Your React app is now live on the internet and anyone can see it.
Type in the IP address of your Droplet in the browser, and you should be able to see and interact with your web app.
You can stop the container by running:
Once stopped, you can also remove the container by running:
You can go a step further and automate this process – write a bash script to integrate your GitHub so that every commit automatically builds an app image and pushes it to the Container Registry. You can also automate pulling the image from the Registry and deploying it to the cloud, creating a CI/CD pipeline of sorts. Tools like Concourse and GitHub Actions are a more sophisticated rendition of exactly this.
Further, you could use Kubernetes to manage a whole slew of containers to manage large-scale, production-grade services. DigitalOcean has it’s own managed Kubernetes that can be seamlessly integrated with DigitalOcean’s Container Registry and unlock the real potential of container-based cloud-native deployments.
You can read more about it here.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!