<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zaimwa9</title>
    <description>The latest articles on DEV Community by Zaimwa9 (@woodz9).</description>
    <link>https://dev.to/woodz9</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F805136%2Fd4381139-d2c6-41f5-91fb-784d92e97050.jpeg</url>
      <title>DEV Community: Zaimwa9</title>
      <link>https://dev.to/woodz9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/woodz9"/>
    <language>en</language>
    <item>
      <title>How to deploy a Dockerized React/TS app in 10 minutes with Koyeb</title>
      <dc:creator>Zaimwa9</dc:creator>
      <pubDate>Fri, 28 Jan 2022 14:58:13 +0000</pubDate>
      <link>https://dev.to/woodz9/how-to-deploy-a-dockerized-reactts-app-in-10-minutes-with-koyeb-13d</link>
      <guid>https://dev.to/woodz9/how-to-deploy-a-dockerized-reactts-app-in-10-minutes-with-koyeb-13d</guid>
      <description>&lt;p&gt;Hola fellow devs!&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
Writing code "is easy", bringing it to life for the first time however might seem scary.&lt;br&gt;
I hope that once this reading is done, serving and deploying your app will no longer be a problem.&lt;/p&gt;

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

&lt;p&gt;Sooo, what will we do today? &lt;/p&gt;

&lt;p&gt;To focus on deploying our application, we'll keep simple  objectives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pop a basic typescript app&lt;/li&gt;
&lt;li&gt;Write and understand our dockerfiles&lt;/li&gt;
&lt;li&gt;Publish our app image on dockerhub&lt;/li&gt;
&lt;li&gt;Deploy our app with &lt;a href="//koyeb.com"&gt;Koyeb&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/signup" rel="noopener noreferrer"&gt;A dockerhub account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker running on your machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://app.koyeb.com/auth/signin" rel="noopener noreferrer"&gt;A koyeb account&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  About Koyeb in a few lines
&lt;/h2&gt;

&lt;p&gt;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.&lt;br&gt;
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 💥)&lt;/p&gt;

&lt;p&gt;Let's see that by ourselves&lt;/p&gt;
&lt;h2&gt;
  
  
  1) Pop the app!
&lt;/h2&gt;

&lt;p&gt;Easy peasy. If as me, you've already done this a thousand of times, just skip this part :).&lt;/p&gt;

&lt;p&gt;Let's create our project using the typescript template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn create react-app my-app --template typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Again, as usual (God I can't take it anymore 😄), a rapid &lt;code&gt;yarn run start&lt;/code&gt; should start your application on &lt;code&gt;http://localhost:3000&lt;/code&gt; with the (My god, yes again) wonderful react app spinner. If not, please advise 😅.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  2) Dockerize our app
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;First, let's create a &lt;code&gt;Dockerfile&lt;/code&gt; file at the root of the project which should look like this, otherwise you cheated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nicely done! (Need help here, I don't know how to add the path in the codeblock)&lt;/p&gt;

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

&lt;p&gt;Having that in mind, we can split into two pieces what we have to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build our application&lt;/li&gt;
&lt;li&gt;Serve our build (we'll use nginx to do so - laziness remember)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Locally we can build our project running &lt;code&gt;npm run build&lt;/code&gt;. Let's see how we translate that into the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1st step: The build&lt;/span&gt;

&lt;span class="c"&gt;# Here we state that we will be using the node 16.10 version as the base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:16.10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="c"&gt;# We define /app as our working directory -where our incoming commands will be executed-&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# We copy our package.json and yarn.lock (adapt if you are using npm to package-lock.json) into our workdir&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; yarn.lock ./&lt;/span&gt;

&lt;span class="c"&gt;# We install our dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn
&lt;span class="c"&gt;# We install react-scripts globally to avoid any bad surprise&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn add react-scripts@3.4.1 &lt;span class="nt"&gt;-g&lt;/span&gt;

&lt;span class="c"&gt;# COPY our app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . ./&lt;/span&gt;

&lt;span class="c"&gt;# And we build! -yarn comes with the node:16.10 image-&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn run build

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;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 &lt;code&gt;nginx/nginx.conf&lt;/code&gt;. I won't go into the details, up to you to deep dive into nginx 😄 so I'll directly share a working config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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;
  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's go back to our &lt;code&gt;Dockerfile&lt;/code&gt; and get this server up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ... Step 1&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick break
&lt;/h2&gt;

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

&lt;p&gt;So, in my opinion, this should be the right time to make sure everything is working as expected.&lt;/p&gt;

&lt;p&gt;We can build or image running the following command &lt;code&gt;docker build . -t frontend:prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;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):&lt;br&gt;
&lt;code&gt;docker run -it --rm -p 1338:80 frontend:prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;it&lt;/code&gt; to run the container interactively&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rm&lt;/code&gt; is to clean up the container once we exit it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;p&lt;/code&gt; the good old port binding, &lt;code&gt;yourmachine:yourcontainer&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Boom, navigate to &lt;code&gt;http://localhost:1338/&lt;/code&gt; and you should have your app up and running -locally-, congrats 🎉!&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Pushing your image to Dockerhub
&lt;/h2&gt;

&lt;p&gt;⚠️ This will push the image on a public repository, if you do not feel at ease, you can follow this &lt;a href="https://stackoverflow.com/questions/28349392/how-to-push-a-docker-image-to-a-private-repository#:~:text=log%20into%20your%20docker%20hub,marked%20as%20private%20by%20default." rel="noopener noreferrer"&gt;stackoverflow guidance&lt;/a&gt; to keep it private.&lt;/p&gt;

&lt;p&gt;I'll assume you created your docker account and remember your &lt;code&gt;DockerId&lt;/code&gt;. Connect to your docker account from the shell with the &lt;code&gt;docker login&lt;/code&gt; command and complete the required steps.&lt;/p&gt;

&lt;p&gt;Let's first tag your image&lt;br&gt;
&lt;code&gt;docker tag frontend:prod {YOUR_DOCKER_ID}/prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and push it (should remind you of git)&lt;br&gt;
&lt;code&gt;docker push {YOUR_DOCKER_ID}/prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That should be it!&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Deploying using Koyeb
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;We'll use their &lt;a href="https://app.koyeb.com/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt; to save time (30 upvotes for CLI).&lt;/p&gt;

&lt;p&gt;You should land on the following page&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42zw092b8urhqb6m7kwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42zw092b8urhqb6m7kwy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on create an app to land on what will be of fun for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec82zv86kt97zlfid4hm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec82zv86kt97zlfid4hm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's in it for us?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the docker method and point to &lt;code&gt;docker.io/{YOUR_DOCKER_ID}/{IMAGE_TAG}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Expose our container port &lt;code&gt;80&lt;/code&gt; (cf: the Dockerfile)&lt;/li&gt;
&lt;li&gt;Choose a name for your service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;Follow the url and here you go :).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Every story (even the worst) has a conclusion. So let's have one too.&lt;/p&gt;

&lt;p&gt;If you went through all this, well first thank you ! Feedback are always welcomed so don't hesitate to point what could be improved 😄.&lt;/p&gt;

&lt;p&gt;Then what have we learnt (I hope):&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Hope this helped!&lt;/p&gt;

&lt;p&gt;And quoting the good old Johnson, what a hell of a ride!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
