<?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>Forem: Mohamed Gallah</title>
    <description>The latest articles on Forem by Mohamed Gallah (@iyadhgallah).</description>
    <link>https://forem.com/iyadhgallah</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%2F657897%2Ffb6ec4d2-ae60-492b-8efb-31bb44186dd0.png</url>
      <title>Forem: Mohamed Gallah</title>
      <link>https://forem.com/iyadhgallah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/iyadhgallah"/>
    <language>en</language>
    <item>
      <title>The JavaScript Runtime Showdown: Node.js vs Deno vs Bun</title>
      <dc:creator>Mohamed Gallah</dc:creator>
      <pubDate>Mon, 13 Oct 2025 16:18:45 +0000</pubDate>
      <link>https://forem.com/iyadhgallah/the-javascript-runtime-showdown-nodejs-vs-deno-vs-bun-4mdj</link>
      <guid>https://forem.com/iyadhgallah/the-javascript-runtime-showdown-nodejs-vs-deno-vs-bun-4mdj</guid>
      <description>&lt;p&gt;Dear fellow developer, are you still using Node/npm for every project? There might be better options.&lt;/p&gt;

&lt;p&gt;The JavaScript runtime landscape has evolved significantly with tools like Bun and Deno (among others) offering better alternatives focusing on performance, security and DX.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait! What are JS runtimes anyway?
&lt;/h3&gt;

&lt;p&gt;Think of runtimes as an environment that enables JS code execution; whether in a browser or on a server like Node.js. A runtime provides the necessary components for JS execution like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JS Engine (V8 in Chrome and Node.js)&lt;/li&gt;
&lt;li&gt;Runtime libraries and APIs for file system and networking etc ...&lt;/li&gt;
&lt;li&gt;The event loop and task queues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enter Node.js
&lt;/h3&gt;

&lt;p&gt;Node.js was released in 2009 to address the limitation of traditional web servers like Apache that struggled with handling large number of simultaneous connections due to blocking I/O model. At the time, Node.js allowed developers to handle thousands of concurrent connections by introducing an event-driven non-blocking I/O model, which makes Node suitable for real-time applications and scalable servers.&lt;/p&gt;

&lt;p&gt;A year later, npm was introduced to handle and simplify dependencies management for Node.js projects as the ecosystem grew significantly. Later, npm became the default package manager for Node.js by making code sharing and modular development more practical.&lt;/p&gt;

&lt;p&gt;Since then, Node became the most used runtime powering millions of applications thanks to a unified JS everywhere architecture for both frontend and backend applications. It matured as a steady, widely adopted backend server, providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extensive packages thanks to the npm ecosystem&lt;/li&gt;
&lt;li&gt;Enterprise-level trust and adoption&lt;/li&gt;
&lt;li&gt;Continuous integration of modern JS patterns standards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, Node.js has notable limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single threaded nature, which limits performance for CPU intensive operations&lt;/li&gt;
&lt;li&gt;Legacy callback-based pattern; having multiple nested callbacks leads to hard to maintain code&lt;/li&gt;
&lt;li&gt;Unstable API changes&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Security issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The security-first alternative: Deno
&lt;/h3&gt;

&lt;p&gt;Deno was developed by Ryan Dahl, the original Node creator, in 2018 to address several issues and design choices in Node.js. It's built on V8 engine and written in Rust.&lt;br&gt;
Deno developers aimed for a more secure, modern  runtime environment by avoiding complexities and security issues in Node.js.&lt;/p&gt;

&lt;p&gt;By doing that, Deno provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incorporates modern JS features like Promises and async/await&lt;/li&gt;
&lt;li&gt;Creates a sandboxed environment with explicit permission for file system access&lt;/li&gt;
&lt;li&gt;Being TypeScript native&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, Deno is still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lacking maturity&lt;/li&gt;
&lt;li&gt;Has a long learning curve&lt;/li&gt;
&lt;li&gt;Small community&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The speedster: Bun
&lt;/h3&gt;

&lt;p&gt;Bun is designed to be a drop-in replacement for Node.js, built to be fast as it's based on JavaScriptCore (unlike Node.js and Deno) and written in Zig.&lt;/p&gt;

&lt;p&gt;It was introduced in 2023, and intended to be fast, high-performance runtime with faster startup times and excellent developer experience.&lt;/p&gt;

&lt;p&gt;Bun provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An all-in-one tools set (bundler, test runner, package manager)&lt;/li&gt;
&lt;li&gt;Native JS, TypeScript and JSX support without external transpilers&lt;/li&gt;
&lt;li&gt;Faster performance in common tasks compared to Node.js&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although, Bun is still in early stages compared to Node and Deno, with smaller community.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Node.js&lt;/th&gt;
&lt;th&gt;Deno&lt;/th&gt;
&lt;th&gt;Bun&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;Moderate (slower startup and dependency install)&lt;/td&gt;
&lt;td&gt;Faster startup and runtime&lt;/td&gt;
&lt;td&gt;Fastest (blazing fast; written in Zig)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosystem Size&lt;/td&gt;
&lt;td&gt;Huge (largest npm ecosystem)&lt;/td&gt;
&lt;td&gt;Growing ecosystem, smaller than Node.js&lt;/td&gt;
&lt;td&gt;Rapidly growing but smaller than Node.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript Support&lt;/td&gt;
&lt;td&gt;Experimental/native transpile requires config or tools like ts-node&lt;/td&gt;
&lt;td&gt;Native support, zero config&lt;/td&gt;
&lt;td&gt;Native support, zero config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security Model&lt;/td&gt;
&lt;td&gt;Low by default; experimental permission model available&lt;/td&gt;
&lt;td&gt;Secure by default: sandbox, explicit permissions for file system and network access&lt;/td&gt;
&lt;td&gt;Moderate; no default sandbox or permission control yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maturity&lt;/td&gt;
&lt;td&gt;Most mature and widely adopted (12+ years)&lt;/td&gt;
&lt;td&gt;Moderately mature since 2020&lt;/td&gt;
&lt;td&gt;Newest (released 2023), rapidly evolving&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Which Should You use?
&lt;/h3&gt;

&lt;p&gt;It depends on what you are building; For enterprise-scale, Node.js remains the wise choice; it's mature, stabel and backed with massive community. Building something security-critical? Deno's ability to provide sandboxed environments is a plus comparing to Node.js.&lt;br&gt;
If performance and modern tooling are your priority, Bun is a worthy choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Honorable mentions
&lt;/h3&gt;

&lt;p&gt;The runtimes landscape extends beyond this trio. Different contexts demand specific solutions such as Edge computing or serverless functions. That's where runtimes like Cloudflare Workers, AWS lambdas, Google Cloud Functions are used to run JS in a serverless environments with rapid startup times and scalability.&lt;br&gt;
IoT and embedded systems also have their runtimes, like JerryScript and DukTape, that are optimized  for microcontrollers and resource restricted devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There is no clear winner here, each runtime is optimized for specific needs and contexts. This specificity and competition drives innovation and pushes features forward as for example how Node.js announced native TypeScript support (as it relied on external transpilers previously), Bun's native support for WebAssembly, and Deno's ability to run JavaScript and TypeScript code in a secure sandboxed environment.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@patrickkonior?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Patrick Konior&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-control-panel-in-a-building-at-night-rDyXca1ZGRI?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>node</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Setting Up a VPS with Nginx, Docker, and GitHub Actions</title>
      <dc:creator>Mohamed Gallah</dc:creator>
      <pubDate>Sat, 24 Aug 2024 10:33:41 +0000</pubDate>
      <link>https://forem.com/iyadhgallah/setting-up-a-vps-with-nginx-docker-and-github-actions-1jb7</link>
      <guid>https://forem.com/iyadhgallah/setting-up-a-vps-with-nginx-docker-and-github-actions-1jb7</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;In this article, we'll be walking through setting up a VPS using Nginx, Docker and Github Actions.&lt;br&gt;
This setup will allow you to deploy your web projects seamlessly as soon as you push code to Github repos.&lt;/p&gt;

&lt;p&gt;We'll be covering up the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up the VPS&lt;/li&gt;
&lt;li&gt;Installing and configuring Nginx&lt;/li&gt;
&lt;li&gt;Installing Docker and setting up your environment.&lt;/li&gt;
&lt;li&gt;Setting up GitHub Actions for continuous deployment.&lt;/li&gt;
&lt;li&gt;Setting up SSL certificates with Let’s Encrypt and auto-renewal.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;p&gt;Before we get started, you'll need the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A VPS with root access. I'll be using a VPS from RackNerd (but you can use any provider:  Hertzner, DigitalOcean, AWS ...).&lt;/li&gt;
&lt;li&gt;A domain name. I'll be using a domain from Namecheap (required for SSL setup).&lt;/li&gt;
&lt;li&gt;Basic knowledge of SSH, Linux commands, and GitHub.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Setting up the VPS
&lt;/h2&gt;

&lt;p&gt;For this article, I’ll assume you’re using Ubuntu as the OS.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SSH into your VPS using the root user.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh root@your_vps_ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Update the package list and upgrade the packages.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Installing and configuring Nginx
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install Nginx.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Start and enable Nginx.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Check the status of Nginx.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open your browser and navigate to your VPS IP address. You should see the Nginx welcome page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure Nginx to serve your web projects. Create a new configuration file for your project.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Update the configuration with your domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;yourdomain.com&lt;/span&gt; &lt;span class="s"&gt;www.yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Save the file and exit the editor. Test the Nginx configuration.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If the test is successful, reload Nginx.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing Docker
&lt;/h2&gt;

&lt;p&gt;(From Docker's official &lt;a href="https://docs.docker.com/engine/install/ubuntu/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setup Docker's &lt;code&gt;apt&lt;/code&gt; repo:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add Docker's official GPG key:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates curl
&lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.asc
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.asc

&lt;span class="c"&gt;# Add the repository to Apt sources:&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install Docker:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify the installation:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
docker-compose &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup GH Actions
&lt;/h2&gt;

&lt;p&gt;In your project's root, create a &lt;code&gt;Dockerfile&lt;/code&gt; and a &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&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="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:14&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&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;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create a &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; file in your project's root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to VPS&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Docker image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker build -t your-app:latest .&lt;/span&gt;
          &lt;span class="s"&gt;docker save your-app:latest &amp;gt; your-app.tar&lt;/span&gt;
          &lt;span class="s"&gt;chmod 664 your-app.tar&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Transfer Docker image to VPS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/scp-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_SSH }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_PORT }}&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-app.tar"&lt;/span&gt;
          &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to VPS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_SSH }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_PORT }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;docker load &amp;lt; /tmp/your-app.tar&lt;/span&gt;
            &lt;span class="s"&gt;docker stop your-app || true&lt;/span&gt;
            &lt;span class="s"&gt;docker rm your-app || true&lt;/span&gt;
            &lt;span class="s"&gt;docker run -d --name your-app -p 8003:8003 your-app:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important 1:&lt;/strong&gt; Replace &lt;code&gt;your-app&lt;/code&gt; with your actual values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important 2:&lt;/strong&gt; Add your VPS IP, ssh key and  username as secrets in your GitHub repo. You'll need to redo this step with each new repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing the Deployment
&lt;/h3&gt;

&lt;p&gt;Push a new commit to the main branch, and GitHub Actions should automatically build and deploy your application to your VPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up SSL
&lt;/h2&gt;

&lt;p&gt;We’ll use Let’s Encrypt, a free SSL/TLS certificate authority, to get a free certificate and set up auto-renewal using `certbot.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Certbot with Nginx plugin:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
sudo apt install certbot python3-certbot-nginx -y&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate the SSL certificate:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With Nginx plugin installed earlier, Certbot will configure Nginx to use the SSL certificate and set up redirects from HTTP to HTTPS. You’ll be prompted to enter your email address and agree to the terms of service.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify SSL installation:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once Certbot completes the process, you can verify that your site is running with HTTPS by visiting &lt;code&gt;https://yourdomain.com&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up auto-renewal:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Certbot will automatically set up a cron job to renew your SSL certificate. You can verify this by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
sudo crontab -l&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see a cron job similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
0 */12 * * * certbot renew --quiet&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup projects after deployment
&lt;/h2&gt;

&lt;p&gt;After deploying your projects, you need to update your nginx configuration to point to the new containers.&lt;/p&gt;

&lt;p&gt;First of all, make sure to change port used by your apps to avoid conflicts.&lt;/p&gt;

&lt;p&gt;Now, given you deployed your app with the name &lt;code&gt;your-app-container&lt;/code&gt;, using the port &lt;code&gt;8002&lt;/code&gt;, you can update your nginx configuration to point to the new container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;nginx&lt;br&gt;
server {&lt;br&gt;
    # Your previous configuration&lt;br&gt;
    # ...&lt;br&gt;
    location /your-app-container {&lt;br&gt;
        rewrite ^/your-app-container/(.*) /$1 break;&lt;br&gt;
        proxy_pass http://localhost:8002;&lt;br&gt;
        proxy_set_header Host $host;&lt;br&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br&gt;
        proxy_set_header X-Forwarded-Proto $scheme;&lt;br&gt;
    }&lt;br&gt;
    # ...&lt;br&gt;
    # SSL configuration&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;You now have a fully automated deployment pipeline for your projects. Every time you push a new commit to the main/master branch, GitHub Actions will build and deploy your application to your VPS. You also have an SSL certificate installed on your server, which will automatically renew every 90 days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;You can take this basic setup further by adding more features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup an advanced Nginx configurtation to handle multiple domains and subdomains or complex load balancing.&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://coolify.io" rel="noopener noreferrer"&gt;Coolify&lt;/a&gt; or similar to create a vercel like deployment pipeline.&lt;/li&gt;
&lt;li&gt;Adding firewall rules for additional security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See ya! 🚀&lt;/p&gt;




&lt;p&gt;Cover image: Photo by &lt;a href="https://unsplash.com/@freeche?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Kvistholt Photography&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/photo-of-computer-cables-oZPwn40zCK4?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>opensource</category>
      <category>docker</category>
      <category>nginx</category>
    </item>
  </channel>
</rss>
