DEV Community

Cover image for How to deploy a Dockerized React/TS app in 10 minutes with Koyeb
Zaimwa9
Zaimwa9

Posted on • Updated on

How to deploy a Dockerized React/TS app in 10 minutes with Koyeb

Hola fellow devs!

If you landed here, it's probably because you know too well how painful and boring it can be to deploy a fully functional React application.
Writing code "is easy", bringing it to life for the first time however might seem scary.
I hope that once this reading is done, serving and deploying your app will no longer be a problem.

Spoiler alert: I'm quite lazy, so we'll stick to the essential meaning we'll build our app on top of create-react-app and I'll assume all the pre-"pre-requisites" are checked (docker concepts, npm and npx installed etc.)

Sooo, what will we do today?

To focus on deploying our application, we'll keep simple objectives:

  • Pop a basic typescript app
  • Write and understand our dockerfiles
  • Publish our app image on dockerhub
  • Deploy our app with Koyeb

Requirements

About Koyeb in a few lines

I've been fed up of using Heroku, even though it does the job for side projects (AWS and GCP a bit overkilled), it was just ... too much and always the same.
Looking for an alternative I stumbled upon Koyeb which provides a serverless platform that allows to deploy apps with low-config, auto-scaling, global scope (in other words, tons of features we won't need here πŸ’₯)

Let's see that by ourselves

1) Pop the app!

Easy peasy. If as me, you've already done this a thousand of times, just skip this part :).

Let's create our project using the typescript template.

yarn create react-app my-app --template typescript
Enter fullscreen mode Exit fullscreen mode

Its name says it all, this will generate a ready to use Typescript project with all dependencies installed (otherwise, don't forget to yarn or npm install at the root of your project)

Again, as usual (God I can't take it anymore πŸ˜„), a rapid yarn run start should start your application on http://localhost:3000 with the (My god, yes again) wonderful react app spinner. If not, please advise πŸ˜….

At this point, you are free to start writing the code you want. We won't go into any coding in this article however.

2) Dockerize our app

In the first version of this post, we'll go straight to a prod-ready environment. But I swear in front of all the gods, if more than 5 of you ask in the comment for a development environment with hot reload... I'll execute myself.

We'll build the dockerfile together, piece after piece. It's never easy to start one from scratch, especially when you want to focus on developing your application so I feel it's important to understand what we want and what we are doing.

First, let's create a Dockerfile file at the root of the project which should look like this, otherwise you cheated:


Enter fullscreen mode Exit fullscreen mode

Nicely done! (Need help here, I don't know how to add the path in the codeblock)

Quick reminder. In production, we do not simply execute our modularized code. We need to build our app first (using npm run build). index.html, our entry file will be served statically. That's where and why going from a local environment to production becomes tricky.

Having that in mind, we can split into two pieces what we have to do:

  1. Build our application
  2. Serve our build (we'll use nginx to do so - laziness remember)

Locally we can build our project running npm run build. Let's see how we translate that into the Dockerfile:

# 1st step: The build

# Here we state that we will be using the node 16.10 version as the base image
FROM node:16.10 as build
# We define /app as our working directory -where our incoming commands will be executed-
WORKDIR /app

# We copy our package.json and yarn.lock (adapt if you are using npm to package-lock.json) into our workdir
COPY package.json ./
COPY yarn.lock ./

# We install our dependencies
RUN yarn
# We install react-scripts globally to avoid any bad surprise
RUN yarn add react-scripts@3.4.1 -g

# COPY our app
COPY . ./

# And we build! -yarn comes with the node:16.10 image-
RUN yarn run build

Enter fullscreen mode Exit fullscreen mode

Alrighty, our build is up and not running. As we said, next step will now consist in mounting a webserver to serve it. Gogogo!

Let's first configure our soon to be born server. To do so, we just need to add the following config file in a new folder nginx/nginx.conf. I won't go into the details, up to you to deep dive into nginx πŸ˜„ so I'll directly share a working config:

server {

  listen 80;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}
Enter fullscreen mode Exit fullscreen mode

Now, let's go back to our Dockerfile and get this server up:

# ... Step 1

# Always good to repeat, we use nginx:stable-alpine as our base image
FROM nginx:stable-alpine
# Taking advantages from docker multi-staging, we copy our newly generated build from /app to the nginx html folder -entrypoint of the webserver-
COPY --from=build /app/build /usr/share/nginx/html
# We copy the nginx conf file from our machine to our image
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
# We expose the port 80 of the future containers
EXPOSE 80
# And finally we can run the nginx command to start the server
CMD ["nginx", "-g", "daemon off;"]
Enter fullscreen mode Exit fullscreen mode

Quick break

Ok! For those still reading, I guess that if you landed on this article, it means you -like me- are no expert in virtualization.

So, in my opinion, this should be the right time to make sure everything is working as expected.

We can build or image running the following command docker build . -t frontend:prod

Take a coffee and once back, if it successfully ran, try the following command to spin up a container (same, for further explanations I'll need 10 upvotes this time):
docker run -it --rm -p 1338:80 frontend:prod

We use:

  • it to run the container interactively
  • rm is to clean up the container once we exit it
  • p the good old port binding, yourmachine:yourcontainer

Boom, navigate to http://localhost:1338/ and you should have your app up and running -locally-, congrats πŸŽ‰!

3) Pushing your image to Dockerhub

⚠️ This will push the image on a public repository, if you do not feel at ease, you can follow this stackoverflow guidance to keep it private.

I'll assume you created your docker account and remember your DockerId. Connect to your docker account from the shell with the docker login command and complete the required steps.

Let's first tag your image
docker tag frontend:prod {YOUR_DOCKER_ID}/prod

and push it (should remind you of git)
docker push {YOUR_DOCKER_ID}/prod

That should be it!

4) Deploying using Koyeb

Koyeb is still in an early-stage, once you have created your account, join their slack and you should be activated within a few minutes.

We'll use their dashboard to save time (30 upvotes for CLI).

You should land on the following page
Image description

Click on create an app to land on what will be of fun for us.

Image description

What's in it for us?

  • Select the docker method and point to docker.io/{YOUR_DOCKER_ID}/{IMAGE_TAG}
  • Expose our container port 80 (cf: the Dockerfile)
  • Choose a name for your service

Create your service ... and TADAAA ! You shouldn't have the time for another coffee that your app should be alive, yes, alive I said (anyways, your last coffee was 5 minutes ago, it would really be unhealthy).

At the time I'm writing this post, custom domains are on their way on Koyeb. However, they will provide you with a subdomain (just like Heroku default you'll tell me).

Follow the url and here you go :).

Conclusion

Every story (even the worst) has a conclusion. So let's have one too.

If you went through all this, well first thank you ! Feedback are always welcomed so don't hesitate to point what could be improved πŸ˜„.

Then what have we learnt (I hope):

  • Run a create-react-app command (ok, doesn't count)
  • Write a simple yet functional Dockerfile (let's not underestimate that, the most complex ones always start somewhere)
  • Build a production-ready React application with docker
  • Starting a nginx webserver with docker
  • Deploy a docker image using Koyeb

Wow, so much. On a more serious tone, first deployments may seem hard but in the end, splitting it into smaller steps helps demystifying them.

Especially as a developer, leveraging tools like Koyeb reduce the complexity of managing a whole infrastructure and let you focus on your field of expertise (coding I guess?) and what really matters: your users.

Hope this helped!

And quoting the good old Johnson, what a hell of a ride!

Discussion (2)

Collapse
woodz9 profile image
Zaimwa9 Author

Wow, amazing. It's crystal clear! Thanks a lot

Collapse
woodz9 profile image
Zaimwa9 Author

Thanks bro for your support! It lightens my heart :)