<?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: Up9t</title>
    <description>The latest articles on DEV Community by Up9t (@up9t).</description>
    <link>https://dev.to/up9t</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%2F1177286%2F7ea0c600-d459-4f5e-bf83-033269ea5690.jpg</url>
      <title>DEV Community: Up9t</title>
      <link>https://dev.to/up9t</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/up9t"/>
    <language>en</language>
    <item>
      <title>Deploying Single Page Application (SPA) or Static Site Generation (SSG) with Nginx</title>
      <dc:creator>Up9t</dc:creator>
      <pubDate>Mon, 23 Feb 2026 06:08:56 +0000</pubDate>
      <link>https://dev.to/up9t/deploying-single-page-application-spa-or-static-site-generation-ssg-with-nginx-3m0b</link>
      <guid>https://dev.to/up9t/deploying-single-page-application-spa-or-static-site-generation-ssg-with-nginx-3m0b</guid>
      <description>&lt;p&gt;Today, I'm going to share a way to deploy single page application or SPA with Nginx. This is a common way to deploy your frontend especially when you work for a small company or startup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's SPA?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Single Page Application (SPA) usually refers to frontend, the web app that only consist of a single HTML page (index.html) but with many javascript scripts. It works by modifies the DOM elements inside it with JavaScript, could be removing, adding and changing HTML elements. &lt;/p&gt;

&lt;p&gt;This is great for user interactivity but not so good for Search Engine Optimizations or SEO (how likely it is for your web app to be shown when you search it up on Google or any other search engines) as most of web crawlers don't execute JavaScript, so it only saw that empty index.html with the title and empty root element &lt;code&gt;&amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you really want to have good SEO for your app, you might need to consider using Server-Side Rendering (SSR) instead, or Static Site Generator (SSG). Thanks to the community we can now have many options of web frameworks that support SSG and SSR while still able to use your favorite stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I know, if I'm using SPA/SSG?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, if you use React or Vue and Vite as a bundler, then you could say that you already use SPA or SSG, you can see the final output inside the &lt;code&gt;dist/&lt;/code&gt; directory after executing the build command &lt;code&gt;npm run build&lt;/code&gt;.&lt;br&gt;
The good thing about this is your app has small in size, and it is portable, you just need the &lt;code&gt;dist/&lt;/code&gt; directory to deploy your app, you don't need the fat &lt;code&gt;node_modules/&lt;/code&gt; or &lt;code&gt;src/&lt;/code&gt; directory anymore, just the &lt;code&gt;dist/&lt;/code&gt; and you're good to deploy.&lt;/p&gt;

&lt;p&gt;The other thing about SPA and SSG is you don't need a server running to make your web works. Well, you still need a server to serve the request from users, but your server doesn't execute the JavaScript, the client (user's browser) does. This is making it possible to deploy them as static files at edge using Content Delivery Network (CDN) for faster retrieval time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;First thing&lt;/strong&gt; you need to have the frontend ready, I mean you don't want to deploy a broken site and hoping people don't see it, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second thing&lt;/strong&gt; you need is a server. A server is basically just a computer, but always active 24/7, doesn't have keyboard, mouse or monitor, just &lt;code&gt;the thing&lt;/code&gt; to do computations, you just need to &lt;code&gt;rent&lt;/code&gt; them from a hosting provider. Although it's possible to build your own server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fcu1lw808vmifg0ymjvrn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fcu1lw808vmifg0ymjvrn.jpg" alt="Server be like" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need a server to make your website accessible to public. Usually, you could just upload your &lt;code&gt;dist/&lt;/code&gt; directory to a CDN service like Cloudflare CDN or AWS CloudFront + S3, but that's not what we're going to do today. We're going to use a self-managed server, which still going to cost you some money.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you can't afford a server just yet, it's fine, you can still run it from your computer, but it won't be accessible to public. Some cloud providers also offer free tier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Third thing&lt;/strong&gt; is a domain (optional). Domain is like this: &lt;code&gt;www.example.com&lt;/code&gt;. When you purchase a server, it doesn't usually come with the domain. You can access it, but only through the IP address of that server.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Before we dive into the steps, let me show you a map to give you a clear picture about what we're trying to achieve.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fnb84kukufwlqmp1o4f9o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fnb84kukufwlqmp1o4f9o.png" alt="Procedure" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Step 1 is building the project, with &lt;code&gt;npm run build&lt;/code&gt;, done.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we go to the 2nd step, copy the build files (dist directory) into the server.&lt;/p&gt;

&lt;p&gt;You can do this with multiple ways, but I'm going to use &lt;code&gt;sftp&lt;/code&gt; here, you need to have &lt;code&gt;ssh&lt;/code&gt; access first to the server before being able to use sftp.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SSH File Transfer Protocol, also known as Secure File Transfer Protocol (SFTP), is a network protocol that provides file access, file transfer, and file management over any reliable data stream. Source: &lt;a href="https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol" rel="noopener noreferrer"&gt;wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using &lt;code&gt;sftp&lt;/code&gt; to transfer file, let's say I have &lt;code&gt;dist/&lt;/code&gt; directory in the &lt;code&gt;/home/yasa/frontend/dist/&lt;/code&gt; and I'm going to put the files in the server user directory as &lt;code&gt;myfiles/&lt;/code&gt; (&lt;code&gt;/home/ubuntu/myfiles/&lt;/code&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using SFTP is like using SSH:
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect sftp&lt;/span&gt;
sftp &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"./Downloads/demopurpose.pem"&lt;/span&gt; ubuntu@98.81.71.240
Connected to 98.81.71.240.

&lt;span class="c"&gt;# Print current server working directory&lt;/span&gt;
sftp&amp;gt; &lt;span class="nb"&gt;pwd
&lt;/span&gt;Remote working directory: /home/ubuntu

&lt;span class="c"&gt;# Upload directory to server with "put -r"&lt;/span&gt;
sftp&amp;gt; put &lt;span class="nt"&gt;-r&lt;/span&gt; /home/yasa/frontend/dist/ myfiles
Uploading /home/yasa/frontend/dist/ to /home/ubuntu/myfiles
Entering /home/yasa/frontend/dist/vite.svg
100% 1497 2.5KB/s 00:00    
Entering /home/yasa/frontend/dist/assets
index-CwjKBBbM.js 
100% 96KB 72.9KB/s 00:01    
index-CtZswya4.css
100% 15KB  53.6KB/s 00:00    
index.html
100% 457 0.8KB/s 00:00    

&lt;span class="c"&gt;# Quit connection&lt;/span&gt;
sftp&amp;gt; quit
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now I have successfully uploaded the files to the server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &amp;amp; Configure Nginx&lt;/p&gt;

&lt;p&gt;Now you have the files on the server, the next step is to install and configure the &lt;code&gt;web server&lt;/code&gt; to &lt;code&gt;serve&lt;/code&gt; those files to the user. There are many options for web server, but I'm going to use &lt;code&gt;Nginx&lt;/code&gt; because it's the most popular, open source and insanely fast too!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Nginx&lt;/strong&gt;: &lt;br&gt;
Installing Nginx is pretty straightforward in most linux distros, here I'm using ubuntu on the server:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login to server&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"./Downloads/demopurpose.pem"&lt;/span&gt; ubuntu@98.81.71.240

&lt;span class="c"&gt;# Update cache&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update 

&lt;span class="c"&gt;# Install nginx&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx

&lt;span class="c"&gt;# Check if nginx service is running&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status nginx

&lt;span class="c"&gt;# Start service if it's not&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start nginx

&lt;span class="c"&gt;# (Optional) Auto start on system reboot&lt;/span&gt;
&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;p&gt;Installing done! Just need to configure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure Nginx&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;These are what we have so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dist/&lt;/code&gt; directory at server &lt;code&gt;/home/ubuntu/myfiles/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;nginx installed on the server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, if nginx installed correctly, you should be able to see the nginx default webpage through the browser, type your Server IP in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fdt8txyzlvolras1yu62z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fdt8txyzlvolras1yu62z.png" alt="Browser shows default nginx page" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It might not show if you have firewall blocking the http port. See how to configure firewall below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have to configure it to show our page instead of the default page:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You can see the default configuration at &lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt; or &lt;code&gt;/etc/nginx/conf.d/default.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# or cat /etc/nginx/sites-available/default&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/nginx/conf.d/default.conf
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Probably look like this:&lt;br&gt;
&lt;/p&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;localhost&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;root&lt;/span&gt;   &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;index&lt;/span&gt;  &lt;span class="s"&gt;index.html&lt;/span&gt; &lt;span class="s"&gt;index.htm&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;p&gt;It's pretty easy to read right?&lt;br&gt;
&lt;/p&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="c1"&gt;# listening on port 80, standard http server.&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="c1"&gt;# you can set it according to your domain (after you do domain registration), &lt;/span&gt;
    &lt;span class="c1"&gt;# usually when you have multiple domains pointing to this server ip.&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt;  &lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="c1"&gt;# url location&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="c1"&gt;# root directory&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt;   &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# automatically looking for index.html or index.htm if not specified in the url.&lt;/span&gt;
        &lt;span class="kn"&gt;index&lt;/span&gt;  &lt;span class="s"&gt;index.html&lt;/span&gt; &lt;span class="s"&gt;index.htm&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;p&gt;First, you can see the root is in &lt;code&gt;/usr/share/nginx/html&lt;/code&gt; or sometimes &lt;code&gt;var/www/html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;root&lt;/span&gt;   &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;You can do it in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Move the &lt;code&gt;dist/&lt;/code&gt; to that directory.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; /path/to/dist /usr/share/nginx/html&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="c"&gt;# or /var/www/html depending on your "root" in the nginx configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change the configuration &lt;code&gt;root&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;root&lt;/span&gt;   &lt;span class="n"&gt;/path/to/your/dist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;I'm usually sticking with the default, so I'm going to leave it as is and move my &lt;code&gt;dist/&lt;/code&gt; to the default directory instead.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; /home/ubuntu/myfiles /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;And then I'm going to add this line in the nginx configuration:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# First attempt to serve request as file, then&lt;/span&gt;
&lt;span class="c1"&gt;# as directory, as url, then fall back to displaying a 404.&lt;/span&gt;
&lt;span class="k"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Edit the configuration file with nano:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/conf.d/default.conf
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;It's going to look like this:&lt;br&gt;
&lt;/p&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;localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# other config  ...&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;root&lt;/span&gt;   &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;index&lt;/span&gt;  &lt;span class="s"&gt;index.html&lt;/span&gt; &lt;span class="s"&gt;index.htm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Added this line&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# other config ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Configure firewall.&lt;/p&gt;

&lt;p&gt;Firewall is a network security system that monitors and controls incoming and outgoing network traffic based on configurable security rules. (Source: &lt;a href="https://en.wikipedia.org/wiki/Firewall_(computing)" rel="noopener noreferrer"&gt;wikipedia&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;There are multiple firewalls available, &lt;code&gt;iptables&lt;/code&gt; and &lt;code&gt;nftables&lt;/code&gt; are usually the built in firewall in linux, so you don't need to install anything, but it is quite low level and not easy to work with. So, I'm going to use the higher level ones, &lt;code&gt;ufw&lt;/code&gt; and &lt;code&gt;firewall-cmd&lt;/code&gt; respectively. You should only use either of them not both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ufw&lt;/strong&gt; (unclomplicated firewall):&lt;br&gt;
This is typically the firewall most Ubuntu user use. &lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;ufw&lt;/code&gt; (if you haven't):&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ufw
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Enable the firewall:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Allowing ssh port (important!):&lt;/p&gt;

&lt;p&gt;Make sure you're allowing the ssh port first before enabling the firewall (it can lock you out of the system). Do as follows:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow ssh
&lt;span class="c"&gt;# or sudo ufw allow 22/tcp&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check firewall status:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status

&lt;span class="c"&gt;# example output:&lt;/span&gt;
Status: active

To                         Action      From
&lt;span class="nt"&gt;--&lt;/span&gt;                         &lt;span class="nt"&gt;------&lt;/span&gt;      &lt;span class="nt"&gt;----&lt;/span&gt;
22/tcp                     ALLOW       Anywhere                  
22/tcp &lt;span class="o"&gt;(&lt;/span&gt;v6&lt;span class="o"&gt;)&lt;/span&gt;                ALLOW       Anywhere &lt;span class="o"&gt;(&lt;/span&gt;v6&lt;span class="o"&gt;)&lt;/span&gt;             
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;In this step you shouldn't be able to access your server through the browser anymore because there is no firewall rule to allow http port, we're going to allow that in the next step.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allowing HTTP port:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow http
&lt;span class="c"&gt;# and allow "https" if necessary&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status
&lt;span class="c"&gt;# example output&lt;/span&gt;
Status: active

To                         Action      From
&lt;span class="nt"&gt;--&lt;/span&gt;                         &lt;span class="nt"&gt;------&lt;/span&gt;      &lt;span class="nt"&gt;----&lt;/span&gt;
22/tcp                     ALLOW       Anywhere                  
80/tcp                     ALLOW       Anywhere                                
22/tcp &lt;span class="o"&gt;(&lt;/span&gt;v6&lt;span class="o"&gt;)&lt;/span&gt;                ALLOW       Anywhere &lt;span class="o"&gt;(&lt;/span&gt;v6&lt;span class="o"&gt;)&lt;/span&gt;             
80/tcp &lt;span class="o"&gt;(&lt;/span&gt;v6&lt;span class="o"&gt;)&lt;/span&gt;                ALLOW       Anywhere &lt;span class="o"&gt;(&lt;/span&gt;v6&lt;span class="o"&gt;)&lt;/span&gt;                     

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


&lt;p&gt;Now you can access your web again in the browser.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;firewalld&lt;/strong&gt;:&lt;br&gt;
Firewalld is usually being used in Red Hat linux distributions, I personally like this more than &lt;code&gt;ufw&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Install, start and enable on start &lt;code&gt;firewalld&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf update
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; firewalld

&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start firewalld
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;firewalld
&lt;/code&gt;&lt;/pre&gt;



&lt;blockquote&gt;
&lt;p&gt;SSH port is usually allowed by default.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Allow HTTP port:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Permanent rule will stay upon restart&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http

&lt;span class="c"&gt;# Permanent config need to reload&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;

&lt;span class="c"&gt;# Check if http service has been allowed&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--list-services&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Now, you could access your web app in the browser once again. &lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;There are more steps you need to do, but I'm not going to cover them in this article, here are some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registering domain for your server.&lt;/li&gt;
&lt;li&gt;Allow port 443 (HTTPS) in the firewall configuration.&lt;/li&gt;
&lt;li&gt;Configuring TLS (after domain registration).&lt;/li&gt;
&lt;li&gt;And many more.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Deploying frontend app (SPA/SSG) in the server is quite easy  with Nginx. You could utilize tools like SFTP to transfer the files to your server, and use UFW/firewall-cmd to easily configure the firewall.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Converting HEIC/HEIF Image in Node.js with Sharp Library</title>
      <dc:creator>Up9t</dc:creator>
      <pubDate>Fri, 10 Oct 2025 02:41:25 +0000</pubDate>
      <link>https://dev.to/up9t/converting-heic-image-extension-in-nodejs-with-the-sharp-library-39mg</link>
      <guid>https://dev.to/up9t/converting-heic-image-extension-in-nodejs-with-the-sharp-library-39mg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Goal: At the end of this article, you should be able to convert heic/heif image extension to jpeg/webp/png in your &lt;code&gt;Nodejs&lt;/code&gt; project without using any external cloud service. I'll provide a practical github repository for this, see the end.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I want to share my experience using the &lt;code&gt;Sharp&lt;/code&gt; library from the &lt;code&gt;Node Package Manager&lt;/code&gt; (NPM) to convert images with heic extension. First, let's talk about the problems. &lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;There are many problems. First is, Why did I have to mind the &lt;code&gt;.heic&lt;/code&gt; extension?&lt;/p&gt;

&lt;p&gt;The problem comes from the &lt;code&gt;iPhone&lt;/code&gt; users, they produce this &lt;code&gt;.heic&lt;/code&gt; extension as the output of the image from their camera. This behaviour is different from an Android phone, which usually outputs &lt;code&gt;.jpeg&lt;/code&gt; or &lt;code&gt;.jpg&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This makes thing more complicated on the &lt;code&gt;backend&lt;/code&gt; side because &lt;code&gt;browsers&lt;/code&gt; don't usually support displaying &lt;code&gt;.heic&lt;/code&gt; image out-of-the-box. As a backend engineer, I'm the one who takes the responsibility for converting the images that aren't supported by the browser to display, to one that is widely compatible. In addition to that, I have to resize the image so it can load faster when displayed on the browser. Most of my customers are using iPhones, that's why I really have to consider this as an issue. &lt;/p&gt;

&lt;p&gt;I had to either convert it to &lt;code&gt;.jpeg&lt;/code&gt; or &lt;code&gt;.webp&lt;/code&gt;. &lt;code&gt;Webp&lt;/code&gt; is a good choice because it has a smaller file size than &lt;code&gt;jpeg&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The other problem is, Sharp library doesn't come out-of-the-box with support for converting the &lt;code&gt;.heic&lt;/code&gt; extension to something else. So, what should I do in this scenario? &lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;On the &lt;code&gt;sharp's&lt;/code&gt; documentation, I was excited to find out that it may has the capability to process images with the &lt;code&gt;heic&lt;/code&gt; extension. It is possible because &lt;code&gt;Sharp&lt;/code&gt; actually uses a library called &lt;code&gt;libvips&lt;/code&gt; as the underlying backend for processing the image. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Libvips&lt;/code&gt; has heic support, but why doesn't my &lt;code&gt;Sharp&lt;/code&gt; library support it?&lt;/p&gt;

&lt;p&gt;That's because, When we install the &lt;code&gt;sharp&lt;/code&gt; library with &lt;code&gt;npm install sharp&lt;/code&gt;, it'll also download the libvips prebuilt binary inside the &lt;code&gt;node_modules&lt;/code&gt; directory. &lt;code&gt;Sharp&lt;/code&gt; will use that binary, but it has limited support for the variety of image extensions. What we can do to make it to have a wider support is to do a custom, manual installation of the &lt;code&gt;libvips&lt;/code&gt;. I'm talking about building &lt;code&gt;libvips&lt;/code&gt; from source. &lt;/p&gt;

&lt;h2&gt;
  
  
  Let's do it!
&lt;/h2&gt;

&lt;p&gt;Essentially, you only need 2 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;code&gt;node-gyp&lt;/code&gt; and &lt;code&gt;node-addon-api&lt;/code&gt; in your existing Node.js project.&lt;/li&gt;
&lt;li&gt;Build &lt;code&gt;libvips&lt;/code&gt; from source and configure &lt;code&gt;sharp&lt;/code&gt; to use our custom built.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Install node-gyp and node-addon in your existing node project.
&lt;/h3&gt;

&lt;p&gt;This step is pretty straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm add node-gyp node-addon-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, first step is done. Now let's go to the second step.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Build &lt;code&gt;libvips&lt;/code&gt; from source and configure &lt;code&gt;sharp&lt;/code&gt; to use our custom built.
&lt;/h3&gt;

&lt;p&gt;Here's how we're going to do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install essential build tools.&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;ninja&lt;/code&gt; &amp;amp; &lt;code&gt;meson&lt;/code&gt; (build from source).&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;libvips&lt;/code&gt; and its dependencies.&lt;/li&gt;
&lt;li&gt;Test the &lt;code&gt;libvips&lt;/code&gt; installation.&lt;/li&gt;
&lt;li&gt;Reconfigure &lt;code&gt;sharp&lt;/code&gt; to use our custom built &lt;code&gt;libvips&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Phew, that's a lot of steps. Let's go through each of them one by one.&lt;/p&gt;

&lt;p&gt;I assume you were using Debian-based linux.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I highly recommend you to use a Docker container with a Debian image. I will provide a fullly working &lt;code&gt;Dockerfile&lt;/code&gt; below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install essential build tools&lt;/p&gt;

&lt;p&gt;Here's where we're going to install git, Python, and other essential build tools.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl python3 git build-essential pkg-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;code&gt;ninja&lt;/code&gt; &amp;amp; &lt;code&gt;meson&lt;/code&gt; (build from source).&lt;/p&gt;

&lt;p&gt;Install ninja:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install ninja&lt;/span&gt;
git clone &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1.13.2 &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/ninja-build/ninja.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ninja &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
./configure.py &lt;span class="nt"&gt;--bootstrap&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ninja &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;ninja /usr/local/bin/ninja
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now, install meson:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install meson&lt;/span&gt;
git clone &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.10.2 &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/mesonbuild/meson.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;meson &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
./packaging/create_zipapp.py &lt;span class="nt"&gt;--outfile&lt;/span&gt; meson.pyz &lt;span class="nt"&gt;--interpreter&lt;/span&gt; &lt;span class="s1"&gt;'/usr/bin/env python3'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x meson.pyz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;meson.pyz /usr/local/bin/meson
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;code&gt;libvips&lt;/code&gt; and its dependencies.&lt;/p&gt;

&lt;p&gt;Let's install its dependencies first.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install build dependencies&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
python3 build-essential pkg-config libglib2.0-dev libexpat1-dev libheif-dev &lt;span class="se"&gt;\&lt;/span&gt;
liblcms2-dev libjpeg-dev libpng-dev libwebp-dev libexif-dev libde265-dev libx265-dev
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now, install and build libvips.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This may take a while&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build libvips&lt;/span&gt;
git clone &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v8.18.2 &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/libvips/libvips.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;libvips &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
meson setup build &lt;span class="nt"&gt;--prefix&lt;/span&gt; /usr/local &lt;span class="nt"&gt;-Dmagick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;disabled &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
meson compile &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
meson &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
meson &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ldconfig &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; .. &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; libvips
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test the &lt;code&gt;libvips&lt;/code&gt; installation.&lt;/p&gt;

&lt;p&gt;At this stage, you will have &lt;code&gt;libvips&lt;/code&gt; installed on your machine, you can test if it was installed properly.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vips &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# vips-8.18.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Check if our vips installation support heif/heic.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vips &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;heif
&lt;span class="c"&gt;# You'll see output something like this:&lt;/span&gt;
&lt;span class="c"&gt;# VipsForeignLoadHeif (heifload_base), load a HEIF image, priority=0&lt;/span&gt;
&lt;span class="c"&gt;# VipsForeignLoadHeifFile (heifload), load a HEIF image (.heic, .heif, .avif), priority=0, is_a, get_flags, header, load&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reconfigure &lt;code&gt;sharp&lt;/code&gt; to use our custom built &lt;code&gt;libvips&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm explore sharp &lt;span class="nt"&gt;--&lt;/span&gt; npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point, you should be able to convert the heic/heif file to webp, jpeg, png and vice versa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;multer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sharp&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sharp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;multer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// ...multer options here with disk storage&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/convert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;single&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formatToMimeType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;jpeg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;png&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormatEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FALLBACK_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;formatToMimeType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;FALLBACK_FORMAT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed to read content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// call sharp as usual.&lt;/span&gt;
  &lt;span class="c1"&gt;// resize the image to 640px height.&lt;/span&gt;
  &lt;span class="c1"&gt;// set either the height or the width, sharp will maintain the aspect ratio.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transformer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;640&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;span class="nf"&gt;toFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formatToMimeType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nx"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed to transform image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// clean up&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minimum frontend example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- index.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:8080/convert"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"format"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"jpeg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;JPEG&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;PNG&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;WEBP&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to check this repository I made, if you find any problem: &lt;a href="https://github.com/up9t/image-conversion-node-demo" rel="noopener noreferrer"&gt;https://github.com/up9t/image-conversion-node-demo&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;

&lt;p&gt;Here's the full Dockerfile for building and running your nodejs app with sharp.&lt;/p&gt;

&lt;p&gt;Make sure you have already include &lt;code&gt;node-gyp&lt;/code&gt;, &lt;code&gt;node-addon-api&lt;/code&gt;, and &lt;code&gt;sharp&lt;/code&gt; packages in your package.json file.&lt;/p&gt;

&lt;p&gt;Here's the Dockerfile looks like:&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;ARG&lt;/span&gt;&lt;span class="s"&gt; NINJA_VERSION=v1.13.2&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; MESON_VERSION=1.10.2&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; LIBVIPS_VERSION=v8.18.2&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:25-trixie&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;base&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:25-trixie-slim&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;base-slim&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;debian:trixie&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;debian-base&lt;/span&gt;

&lt;span class="c"&gt;# Install meson and ninja&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;debian-base&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;install-tools&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NINJA_VERSION&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; MESON_VERSION&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /build&lt;/span&gt;
&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl python3 git build-essential pkg-config
&lt;span class="c"&gt;# Install ninja&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NINJA_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/ninja-build/ninja.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;cd &lt;/span&gt;ninja &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    ./configure.py &lt;span class="nt"&gt;--bootstrap&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chmod&lt;/span&gt; +x ninja &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;mv &lt;/span&gt;ninja /usr/local/bin/ninja
&lt;span class="c"&gt;# Install meson&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MESON_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/mesonbuild/meson.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;cd &lt;/span&gt;meson &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    ./packaging/create_zipapp.py &lt;span class="nt"&gt;--outfile&lt;/span&gt; meson.pyz &lt;span class="nt"&gt;--interpreter&lt;/span&gt; &lt;span class="s1"&gt;'/usr/bin/env python3'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chmod&lt;/span&gt; +x meson.pyz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;mv &lt;/span&gt;meson.pyz /usr/local/bin/meson


&lt;span class="c"&gt;# Download libvips&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;base&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;download-vips&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; LIBVIPS_VERSION&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /downloads&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    curl git
&lt;span class="k"&gt;RUN &lt;/span&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LIBVIPS_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/libvips/libvips.git


&lt;span class="c"&gt;# Build libvips&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;base&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-vips &lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /libvips&lt;/span&gt;
&lt;span class="c"&gt;# Install build dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    python3 build-essential pkg-config libglib2.0-dev libexpat1-dev libheif-dev &lt;span class="se"&gt;\
&lt;/span&gt;    liblcms2-dev libjpeg-dev libpng-dev libwebp-dev libexif-dev libde265-dev libx265-dev
&lt;span class="c"&gt;# Copy build tools from the install-tools stage&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=install-tools /usr/local/bin/ninja /usr/local/bin/ninja&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=install-tools /usr/local/bin/meson /usr/local/bin/meson&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=download-vips /downloads/libvips .&lt;/span&gt;
&lt;span class="c"&gt;# Build libvips&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;meson setup build &lt;span class="nt"&gt;--prefix&lt;/span&gt; /usr/local &lt;span class="nt"&gt;-Dmagick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;disabled &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    meson compile &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    meson &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;DESTDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/vips meson &lt;span class="nb"&gt;install&lt;/span&gt;


&lt;span class="c"&gt;# Install node modules and build&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;base&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;builder&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; --from=build-vips /opt/vips/usr/local /usr/local&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;ldconfig
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./package.json ./package-lock.json .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="c"&gt;# Rebuild sharp if necessary, the doc says it automatically detects the vips global installation&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm explore sharp &lt;span class="nt"&gt;--&lt;/span&gt; npm run build
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="c"&gt;# Remove/comment the line below if you don't need npm run build for your project&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build


&lt;span class="c"&gt;# Run&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;base-slim&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;runner&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; USERNAME=node&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;# Install runtime dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    libglib2.0-0 libexpat1 libheif1 liblcms2-2 libjpeg62-turbo &lt;span class="se"&gt;\
&lt;/span&gt;    libpng16-16 libwebp7 libexif12 libde265-0 libx265-215 &lt;span class="se"&gt;\
&lt;/span&gt;    libfftw3-double3 libwebpmux3 libwebpdemux2 libpangocairo-1.0-0 libpango-1.0-0 &lt;span class="se"&gt;\
&lt;/span&gt;    libcairo2 libpangoft2-1.0-0 libfontconfig1 libtiff6 librsvg2-2 libopenexr-3-1-30 libopenjp2-7 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-vips /opt/vips/usr/local /usr/local&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;ldconfig
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; /app
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/node_modules/sharp ./node_modules/sharp&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; ${USERNAME}&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["npm"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The default sharpjs package doesn't include vips with heic support, it's up to the developer to build vips from source with wider support.&lt;/p&gt;

&lt;p&gt;The above Dockerfile is a good starting point to build a &lt;code&gt;Node.js&lt;/code&gt; app with &lt;code&gt;Sharp&lt;/code&gt; and &lt;code&gt;Vips&lt;/code&gt; support for &lt;code&gt;heic&lt;/code&gt; images.&lt;/p&gt;

&lt;p&gt;Checkout my github repository if you want to look more into the code in practical: &lt;a href="https://github.com/up9t/image-conversion-node-demo" rel="noopener noreferrer"&gt;https://github.com/up9t/image-conversion-node-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>programming</category>
    </item>
    <item>
      <title>How Does BCrypt Verification Work?</title>
      <dc:creator>Up9t</dc:creator>
      <pubDate>Thu, 20 Mar 2025 23:58:43 +0000</pubDate>
      <link>https://dev.to/up9t/how-does-bcrypt-verification-work-g19</link>
      <guid>https://dev.to/up9t/how-does-bcrypt-verification-work-g19</guid>
      <description>&lt;p&gt;Have you ever wondered how can a hashing algorithm verify? &lt;/p&gt;

&lt;p&gt;Before we going into that, we have to know that our password that we used to enter on a website, is going through one-way hashing function, thanks to it, your password is only known by ourself, even the server's owner &lt;strong&gt;shouldn't know&lt;/strong&gt; about it. &lt;/p&gt;

&lt;p&gt;Differs from &lt;code&gt;encryption&lt;/code&gt; which is designed to be able to &lt;code&gt;decrypt&lt;/code&gt; the contents, &lt;code&gt;Hashing&lt;/code&gt; is only one way, that mean you hash it, you can't no longer retrieve it's original content. How does that work?&lt;/p&gt;

&lt;h2&gt;
  
  
  How does hashing work?
&lt;/h2&gt;

&lt;p&gt;Before we learn about Bcrypt hashing and verification, I will try to explain to you a simple hashing concepts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let's say you have a password, your password is &lt;code&gt;pass123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Each letter will be then transform into another letter using some algorithms. For example &lt;code&gt;pass123&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt; transform to &lt;code&gt;m&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt; transform to &lt;code&gt;c&lt;/code&gt; and so on. But it's only one way, if &lt;code&gt;p&lt;/code&gt; transform to &lt;code&gt;m&lt;/code&gt;, doesn't mean that &lt;code&gt;m&lt;/code&gt; will transform into &lt;code&gt;p&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your password: &lt;code&gt;pass123&lt;/code&gt; might transform into &lt;code&gt;D32rASDF&lt;/code&gt; and will always transfrom into &lt;code&gt;D32rASDF&lt;/code&gt; everytime you enter &lt;code&gt;pass123&lt;/code&gt;. So in practice, you can compare it, like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input_password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stored_password_in_database&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// user login succeed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;WAIT A MINUTE!&lt;/strong&gt;, But in advance hashing algorithm like &lt;code&gt;BCrypt&lt;/code&gt;, &lt;code&gt;Argon2&lt;/code&gt; and any other strong hashing algorithm will produce different hash result, even if you pass the same password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nf"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pass123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// result: $2a$12$G/bzOjiOAeFTHhvvImkRMeXQ9IiMK6Z38wcMs4H1W0wQ.ry7Loc1a&lt;/span&gt;

&lt;span class="nf"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pass123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// result: $2a$12$e1sitHZPq0KFuJ.G9dOmn.wKtfEwVp5O8qElZ.xzqOmQ5ZmA6BYm6&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Then, this will never work again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pass123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pass123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// user login always failed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  How does it verify then?
&lt;/h2&gt;

&lt;p&gt;It actually simple, instead of having your password passed straight  into a hash function and get a result. It has another variable that stores random letters, this variable is called &lt;code&gt;salt&lt;/code&gt;, this salt will help hashing algorithm makes more unpredictable result, they also have &lt;code&gt;cost&lt;/code&gt; variable. Cost is the measure of the resources needed to calculate a hash.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://en.wikipedia.org/wiki/Bcrypt" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;, this is the structure of bcrypt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$2&amp;lt;a/b/x/y&amp;gt;$[cost]$[22 character salt][31 character hash]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, with input password &lt;code&gt;abc123xyz&lt;/code&gt;, cost &lt;code&gt;12&lt;/code&gt;, and a random &lt;code&gt;salt&lt;/code&gt;, the output of bcrypt is the string&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$2a$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
\__/\/ \____________________/\_____________________________/
Alg Cost      Salt                        Hash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$2a$&lt;/code&gt;: The hash algorithm identifier (bcrypt)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;12&lt;/code&gt;: Input cost (2^12 i.e. 4096 rounds)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;R9h/cIPz0gi.URNNX3kh2O&lt;/code&gt;: A base-64 encoding of the input salt&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PST9/PgBkqquzi.Ss7KIUgO2t0jWMUW&lt;/code&gt;: A base-64 encoding of the first 23 bytes of the computed 24 byte hash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see from above, BCrypt stores the &lt;code&gt;algorithm&lt;/code&gt;, &lt;code&gt;cost&lt;/code&gt;, and &lt;code&gt;salt&lt;/code&gt; alongside the &lt;code&gt;hash&lt;/code&gt; result. You couldn't get bcrypt to work with only the &lt;code&gt;hash&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Important note:
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If you had the &lt;code&gt;algorithm&lt;/code&gt;, &lt;code&gt;cost&lt;/code&gt;, and &lt;code&gt;salt&lt;/code&gt;, you could get the same hashing result, this mean you could compare it&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take a look at this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bcryptjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// your input&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pass123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// implement the function&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bcryptHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;genSaltSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hashSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// generate hash by calling the function&lt;/span&gt;
&lt;span class="c1"&gt;// this result is then stored on the database&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bcryptHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPassword&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// print to the console&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I execute the same file, it will of course generate 2 different hash&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;span class="c"&gt;# result = $2b$12$XfT3eq.O3t.NeklIEbzgKOfLZZgHSrYSBKNk5.f5IngnP/Z6/Xo/u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;span class="c"&gt;# result = $2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But, it has some similarities, it starts with &lt;code&gt;$2b$12$&lt;/code&gt;, which the &lt;code&gt;algorithm&lt;/code&gt; followed by the &lt;code&gt;cost&lt;/code&gt;, then the 22 letter after is the &lt;code&gt;salt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;From the result, we now know that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Algorithm&lt;/code&gt; is start with &lt;code&gt;2b&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cost&lt;/code&gt; is &lt;code&gt;12&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We need to the &lt;code&gt;salt&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// assume this is the string of password that you get from the database&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storedPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// remove the prefix, then get the 22 letters of salt&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;storedPassword&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$2b$12$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// salt = "FSmg5sXtB.XdJH2fFTrV7u"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The salt is: &lt;code&gt;FSmg5sXtB.XdJH2fFTrV7u&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, instead of generating a new &lt;code&gt;salt&lt;/code&gt;, you assign the &lt;code&gt;salt&lt;/code&gt; that is extracted, and combines it with algorithm and the cost at the suffix&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// this will give you fixed hash result&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bcryptFixedHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// const cost = 12;&lt;/span&gt;
    &lt;span class="c1"&gt;// const salt = bcrypt.genSaltSync(cost);&lt;/span&gt;

    &lt;span class="c1"&gt;// combines the alg, cost and salt together&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$2b$12$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FSmg5sXtB.XdJH2fFTrV7u&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hashSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hash&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pass123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storedPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bcryptFixedHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPassword&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// now you get the exact same hash&lt;/span&gt;
&lt;span class="c1"&gt;// inputHash = $2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Comparing the hash
&lt;/h2&gt;

&lt;p&gt;Now that you get the same hash, you could compare it to verify the user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storedPassword&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// user login succeed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In addition, you could use the safe compare equal function rather than &lt;code&gt;===&lt;/code&gt; to compare, &lt;strong&gt;but that wouldn't be neccesary&lt;/strong&gt; since the bcrypt hash result is always different and thus &lt;strong&gt;irrelevant to timing attack&lt;/strong&gt; or similar.&lt;/p&gt;

&lt;p&gt;Thank you!&lt;/p&gt;

</description>
      <category>hashing</category>
      <category>security</category>
      <category>authentication</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Laravel 11+: Use Minio for File Storage as AWS S3 Free Alternative</title>
      <dc:creator>Up9t</dc:creator>
      <pubDate>Fri, 28 Feb 2025 14:18:29 +0000</pubDate>
      <link>https://dev.to/up9t/laravel-11-use-minio-for-file-storage-as-aws-s3-free-alternative-1bem</link>
      <guid>https://dev.to/up9t/laravel-11-use-minio-for-file-storage-as-aws-s3-free-alternative-1bem</guid>
      <description>&lt;p&gt;We might begin with stores our files like images on the filesystem. But when the times our application got bigger, we might want to separate things up, by using a dedicated service to stores objects. &lt;/p&gt;

&lt;p&gt;For example, the most common way to store objects which is &lt;strong&gt;AWS's Simple Storage Service&lt;/strong&gt; or &lt;strong&gt;S3&lt;/strong&gt; to be short. But that comes with the downside. Since it was a third-party service, we can't just use it for free. (Actually, they have free tier but with limited spaces)&lt;/p&gt;

&lt;p&gt;Fortunately, there is a &lt;strong&gt;Free&lt;/strong&gt;, &lt;strong&gt;Open Source&lt;/strong&gt; and &lt;strong&gt;S3 Compatible&lt;/strong&gt; alternative called &lt;strong&gt;Minio&lt;/strong&gt;. With it, you can self-host the S3 compatible storage on your own.&lt;/p&gt;




&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;




&lt;h3&gt;
  
  
  Minio
&lt;/h3&gt;

&lt;p&gt;First, we need to have &lt;em&gt;Minio&lt;/em&gt;, we can install it in a variety of ways. In this example, I will use &lt;strong&gt;Docker&lt;/strong&gt;. If you don't want to use docker, you should check the &lt;a href="https://min.io/docs/minio/linux" rel="noopener noreferrer"&gt;Official Documentation&lt;/a&gt;.&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="c1"&gt;# compose.yaml&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;Laravel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# build:&lt;/span&gt;
    &lt;span class="c1"&gt;#   context: .&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-network&lt;/span&gt; &lt;span class="c1"&gt;# use the same network as minio&lt;/span&gt;

  &lt;span class="c1"&gt;# Here's our minio&lt;/span&gt;
  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quay.io/minio/minio:RELEASE.2025-02-03T21-03-04Z&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;command&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;server"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/data"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--console-address=:9001"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-network&lt;/span&gt; &lt;span class="c1"&gt;# use the same network as your app&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./secrets/minio.env&lt;/span&gt; &lt;span class="c1"&gt;# we will specify this file later&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;object-storage:/data&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;my-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# declare a network&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;object-storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# for persistent data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me brief explain what's on the &lt;code&gt;storage&lt;/code&gt; section above:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;image: quay.io/minio/minio:RELEASE.2025-02-03T21-03-04Z&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;for the image we are going to pull from the &lt;em&gt;quay&lt;/em&gt; registry, and the tag we are going to use is &lt;code&gt;RELEASE.2025-02-03T21-03-04Z&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can use other tags too for example &lt;code&gt;latest&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;command: ["server", "/data", "--console-address=:9001"]&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;first it will execute command &lt;code&gt;server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;then the storage path will be on &lt;code&gt;/data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;the &lt;code&gt;--console-address=:9001&lt;/code&gt; meaning that will expose the console or the UI on the port 9001, while the API was exposed on port 9000 (by default)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We also need to specify the environment variable, but we're going to ignore it for now.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Laravel
&lt;/h3&gt;

&lt;p&gt;On our Laravel's application, we need to install the AWS S3 packages using composer. Type the following on terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require league/flysystem-aws-s3-v3 &lt;span class="s2"&gt;"^3.0"&lt;/span&gt; &lt;span class="nt"&gt;--with-all-dependencies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;We now need to do some configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minio
&lt;/h3&gt;

&lt;p&gt;On Minio's side we need to create root user. We will later use that user to create a new user specifically for our application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A root user is the highest level of access you can have on a computer system. It is essentially the "superuser" with the ability to perform any task, including those that are restricted to normal users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To create that, we have to provide 2 variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MINIO_ROOT_USER&lt;/code&gt;: the name for the root user&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MINIO_ROOT_PASSWORD&lt;/code&gt;: the password for the root user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you take a look at the &lt;code&gt;compose.yaml&lt;/code&gt; file above, you will see this:&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;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./secrets/minio.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are saying that the variables will be stored on a file called &lt;code&gt;minio.env&lt;/code&gt; that is on &lt;code&gt;secrets&lt;/code&gt; directory. Let's create one, make sure it's on the same level as the &lt;code&gt;compose.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;create .env file for minio&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;secrets &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;minio.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the new created &lt;code&gt;minio.env&lt;/code&gt; file, fill with these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MINIO_ROOT_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root &lt;span class="c"&gt;# can be whatever you want&lt;/span&gt;
&lt;span class="nv"&gt;MINIO_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;insert-whatever-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can login to the &lt;strong&gt;Minio's Console&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the browser, go to &lt;a&gt;localhost:9000&lt;/a&gt; or &lt;a&gt;localhost:9001&lt;/a&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Doesn't matter which port. If you use browser, &lt;code&gt;minio&lt;/code&gt; will redirect you to port 9001 even if you type the port &lt;code&gt;9000&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's going to look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fe9qdx08xu2urncib0yv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fe9qdx08xu2urncib0yv9.png" alt="Minio's login page" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Login using the root username and password that we typed on the &lt;code&gt;minio.env&lt;/code&gt; file previously. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating User&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After successful login, go to the &lt;code&gt;Identify -&amp;gt; User&lt;/code&gt; Section, and try to create new user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F87xajy9idtz7p2kc1o0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F87xajy9idtz7p2kc1o0h.png" alt="User Section" width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dont forget to check on the &lt;code&gt;Read Write&lt;/code&gt; policy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fv6gvo8bk5tynv1jjm7ld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fv6gvo8bk5tynv1jjm7ld.png" alt="Create User Menu" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, you need to create a bucket for your application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is a bucket?&lt;br&gt;
Just that imagine that you have a bucket, then you can put your toys, your stuff in it. In this case we store our object like files or images.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go to the &lt;code&gt;Buckets&lt;/code&gt; section and click on the &lt;code&gt;Create Bucket&lt;/code&gt; button&lt;/p&gt;

&lt;p&gt;Type on the bucket name field, then click &lt;code&gt;Create Bucket&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F1mwj5a0svqsryr1gdsaw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F1mwj5a0svqsryr1gdsaw.png" alt="Bucket Section" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel
&lt;/h3&gt;

&lt;p&gt;On &lt;strong&gt;Laravel&lt;/strong&gt; you need to change the AWS variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_ACCESS_KEY_ID=username
AWS_SECRET_ACCESS_KEY=password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;you can ignore the &lt;code&gt;AWS_DEFAULT_REGION&lt;/code&gt; and let it be there&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fill the bucket name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_BUCKET=bucket-name-here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you need to change the &lt;code&gt;AWS_ENDPOINT&lt;/code&gt; to point to your storage service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_ENDPOINT="http://storage:9000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also you need to create another variable called &lt;code&gt;AWS_URL&lt;/code&gt;. It's basically the endpoint with bucket name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_URL="http://storage:9000/bucket-name-here"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Please note that I'm using the name service from the compose.yaml file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Last, but not least, you need to change the &lt;code&gt;AWS_USE_PATH_STYLE_ENDPOINT&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Here's what our variables look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;=&lt;span class="n"&gt;username&lt;/span&gt;
&lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;=&lt;span class="n"&gt;password&lt;/span&gt;
&lt;span class="n"&gt;AWS_DEFAULT_REGION&lt;/span&gt;=&lt;span class="n"&gt;us&lt;/span&gt;-&lt;span class="n"&gt;east&lt;/span&gt;-&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;AWS_BUCKET&lt;/span&gt;=&lt;span class="n"&gt;bucketname&lt;/span&gt;
&lt;span class="n"&gt;AWS_USE_PATH_STYLE_ENDPOINT&lt;/span&gt;=&lt;span class="n"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;AWS_ENDPOINT&lt;/span&gt;=&lt;span class="s2"&gt;"http://storage:9000"&lt;/span&gt;
&lt;span class="n"&gt;AWS_URL&lt;/span&gt;=&lt;span class="s2"&gt;"http://storage:9000/bucketname"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you have to change they way you upload image.&lt;/p&gt;

&lt;p&gt;From this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// controller&lt;/span&gt;

&lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;storePublicly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"images"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// controller&lt;/span&gt;

&lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;storePublicly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"images"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;disk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"s3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Make bucket publicly accessible
&lt;/h3&gt;

&lt;p&gt;You can't just paste the url on the browser, it won't let you to view the content of the file. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;failed to get Stat() response from server for 1-5000x3333.jpg (version null): Access Denied.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, you need to change the setting of the bucket so that everyone can see the content.&lt;/p&gt;

&lt;p&gt;On the &lt;code&gt;Buckets&lt;/code&gt; menu, click on your bucket, then change the &lt;code&gt;Access Policy&lt;/code&gt; to &lt;code&gt;public&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ftvw30izwfq06hty75uul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ftvw30izwfq06hty75uul.png" alt="Bucket Setting" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, your image should be accessible.&lt;/p&gt;

&lt;p&gt;Comment if you make it here!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>s3</category>
      <category>php</category>
      <category>docker</category>
    </item>
    <item>
      <title>Learn SSH with Docker</title>
      <dc:creator>Up9t</dc:creator>
      <pubDate>Thu, 20 Feb 2025 12:05:08 +0000</pubDate>
      <link>https://dev.to/up9t/learn-ssh-with-docker-5e1a</link>
      <guid>https://dev.to/up9t/learn-ssh-with-docker-5e1a</guid>
      <description>&lt;p&gt;&lt;strong&gt;N&lt;/strong&gt;ormally, we use &lt;strong&gt;S&lt;/strong&gt;ecure &lt;strong&gt;Sh&lt;/strong&gt;ell (&lt;strong&gt;SSH&lt;/strong&gt;) to access a server, but not all people have a server; one of the main reasons is because it's not cheap to buy a &lt;br&gt;
&lt;strong&gt;V&lt;/strong&gt;irtual &lt;strong&gt;P&lt;/strong&gt;rivate &lt;strong&gt;S&lt;/strong&gt;erver (&lt;strong&gt;VPS&lt;/strong&gt;). But the knowledge about how to use SSH is really important, and all programmers should know about it.&lt;/p&gt;

&lt;p&gt;Fortunately, we can use something that can act like a client and a server. Because a server is basically just another computer, we can use virtualization technology like Virtual Machine (VM) or Container like Docker to learn more about SSH.&lt;/p&gt;
&lt;h2&gt;
  
  
  Concept
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open first terminal then run Ubuntu image within Docker container, then install SSH client, this is act like our local machine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open second terminal then run another Ubuntu image within Docker container, then install SSH server, this will be the server that we want to control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Link those containers using the same network.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;First thing you need is Docker, you can install docker in various way depends on your machine. If you're on Windows, you can install &lt;a href="https://docs.docker.com/desktop/setup/install/windows-install/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;, for others instalation please refers to the &lt;a href="https://docs.docker.com/get-started/get-docker/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Additionally for Windows users, after the installation, you might want to install Windows Subsystem for Linux (WSL) and integrate with your Docker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open your terminal, then type &lt;code&gt;docker version&lt;/code&gt; to make sure docker is running.&lt;/p&gt;
&lt;h2&gt;
  
  
  First Step
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Prepare the client
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal, then run Ubuntu image.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client ubuntu:latest bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker&lt;/code&gt; the base command&lt;br&gt;
&lt;code&gt;run&lt;/code&gt; tell docker to run container from image&lt;br&gt;
&lt;code&gt;--rm&lt;/code&gt; used to automatically delete container after not using it&lt;br&gt;
&lt;code&gt;-it&lt;/code&gt; combinations from --interactive and --tty&lt;br&gt;
&lt;code&gt;ubuntu:latest&lt;/code&gt; the name of the image that we will run, we are using image named 'ubuntu' and tag 'latest', which mean the latest version of ubuntu&lt;br&gt;
&lt;code&gt;bash&lt;/code&gt; command that we want to execute to the container, this will open bash terminal&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;After succeeding, you'are now inside the container. Because it is isolated, you can do whatever you want in there, now we need to install SSH client
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; openssh-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: you have to do &lt;code&gt;apt update&lt;/code&gt; first, before install a package&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Prepare the host
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;While the previous terminal opened, open another terminal, and run another Ubuntu image.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;server ubuntu:latest bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;You'll notice this step will be much faster, because we have already downloaded the image from the first step, and docker just need to running it instead of needed to download again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Install SSH server
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; openssh-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Beginning
&lt;/h2&gt;

&lt;p&gt;Okay it just the beginning, what do we have so far?&lt;/p&gt;

&lt;p&gt;We now have a local machine (client) and a host machine (server), that's just like a real-life scenario. &lt;/p&gt;
&lt;h2&gt;
  
  
  Learn time
&lt;/h2&gt;

&lt;p&gt;So, &lt;strong&gt;how does it work&lt;/strong&gt;? &lt;/p&gt;

&lt;p&gt;As we can see, the server is installed the &lt;code&gt;openssh-server&lt;/code&gt; and not &lt;code&gt;openssh-client&lt;/code&gt;, what's the difference?&lt;/p&gt;

&lt;p&gt;Well, when we install the &lt;code&gt;openssh-server&lt;/code&gt;, it will install an SSH service, that service will always running and listening, always ready if there are clients that want to be connected to our server. By default, it is listening on port 22.&lt;/p&gt;

&lt;p&gt;Different from SSH server, SSH client does not listen on any port on our machine, it simply to connect our local machine to the server that has SSH service running on it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Let's add security
&lt;/h3&gt;

&lt;p&gt;Wait, wouldn't the other people also able to connect to our server and control them? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're right!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;But don't worry, we also have been thinking about that and that's why there are additional steps to make our server safe and still accessible by us, but not by others. &lt;em&gt;Pstt..&lt;/em&gt; It's called &lt;strong&gt;encryption&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;To have a better understanding about how it works, let's just started.&lt;/p&gt;
&lt;h3&gt;
  
  
  Key Generation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;On the client container (hope you remember which one), we will generate a key-pair using this command.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ssh-keygen&lt;/code&gt; command to generate ssh keypair&lt;br&gt;
&lt;code&gt;-t ed25519&lt;/code&gt; specify the used algorithm, this example we use &lt;code&gt;ed25519&lt;/code&gt;, another popular option is &lt;code&gt;rsa&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You'll be prompted to specify the output file, just press enter on your keyboard to use the default location. &lt;/p&gt;

&lt;p&gt;They will also ask you to enter a passphrase and confirmation passphrase, just leave them blank and then press enter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If successful, they will generate 2 files: &lt;code&gt;id_ed25519&lt;/code&gt; and &lt;code&gt;id_ed25519.pub&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The one with the &lt;code&gt;.pub&lt;/code&gt; suffix on their name is the &lt;code&gt;public key&lt;/code&gt;, you need to keep this file on your host (server)&lt;/p&gt;

&lt;p&gt;The other file is called &lt;code&gt;private key&lt;/code&gt;, it's kept on the local machine (client)&lt;/p&gt;

&lt;p&gt;Since you're a root user, those files will be stored on directory &lt;code&gt;/root/.ssh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;you can see them using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /root/.ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Note
&lt;/h3&gt;

&lt;p&gt;Okay, from now on, I will just simply called &lt;code&gt;local machine&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;local machine&lt;/code&gt; is the one that you install the &lt;code&gt;openssh-client&lt;/code&gt;&lt;br&gt;
&lt;code&gt;server&lt;/code&gt; is the one that you install the &lt;code&gt;openssh-server&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Public &amp;amp; Private Key
&lt;/h3&gt;

&lt;p&gt;As I said before, you need to keep the private key &lt;em&gt;only on local machine&lt;/em&gt; , while store the public key on your &lt;strong&gt;server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are several ways to move your public key to the &lt;em&gt;server&lt;/em&gt;, one common way and the way that is use is simply: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;copy the content of public key to your clipboard.&lt;/li&gt;
&lt;li&gt;create new file in the &lt;em&gt;server&lt;/em&gt; and then paste the content to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Alright, let's try it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The following command will output the content of public key file (id_ed25519.pub) then and I want you to manually select and copy the text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /root/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you copy, it should be on your clipboard now. Then switch to the &lt;em&gt;server&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First, create a file called &lt;code&gt;id_ed25519.pub&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /root/.ssh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; /root/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you need to open that file and paste. But wait, since we don't have any editor installed, we have to first install a lightweight text editor called &lt;code&gt;nano&lt;/code&gt;, there are other options too like &lt;code&gt;vi&lt;/code&gt; and &lt;code&gt;vim&lt;/code&gt; but &lt;code&gt;nano&lt;/code&gt; is more beginner friendly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nano
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have &lt;code&gt;nano&lt;/code&gt; installed on the server, we can now open that file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano /root/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can paste the content from your clipboard (CTRL+V) then save (CTRL+S) and exit (CTRL+X)&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Step
&lt;/h3&gt;

&lt;p&gt;Okay, we have installed the public key in the &lt;em&gt;server&lt;/em&gt;, so can we access the server now?&lt;/p&gt;

&lt;p&gt;Not yet, but we're almost there.&lt;/p&gt;

&lt;p&gt;We have to make sure that the server is reachable by our local machine.&lt;/p&gt;

&lt;p&gt;Usually, we do this by using &lt;code&gt;ping&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reaching the Server
&lt;/h3&gt;

&lt;p&gt;On the &lt;em&gt;local machine&lt;/em&gt;, install the tool using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; iputils-ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can check if the server is within our reach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we got no responses which indicates that the &lt;em&gt;server&lt;/em&gt; wasn't reachable from our &lt;em&gt;local machine&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;*&lt;em&gt;But why? *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well, containers can't communicate directly because they are &lt;em&gt;isolated&lt;/em&gt; from each other by default. You have to attach them to the same network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attach to the same network
&lt;/h3&gt;

&lt;p&gt;Alright, I want you to open new terminal, then do the following.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a network&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This will create a network called 'my-ssh-network'&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create my-ssh-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;docker&lt;/code&gt; this is the main command&lt;br&gt;
&lt;code&gt;network&lt;/code&gt; subcommand&lt;br&gt;
&lt;code&gt;create&lt;/code&gt; used for create something&lt;br&gt;
&lt;code&gt;my-ssh-network&lt;/code&gt; this is the name of network that we want to create, it's up to you&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attach network to the client container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network connect my-ssh-network client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Attach network to the server&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network connect my-ssh-network server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Let's try again
&lt;/h3&gt;

&lt;p&gt;From the &lt;em&gt;local computer&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see line by line from the terminal comes out one at the time, that mean it received feedback from the server! &lt;/p&gt;

&lt;p&gt;Hey-hey, do you what that mean?&lt;/p&gt;

&lt;p&gt;We can finally connected to it!&lt;/p&gt;

&lt;h3&gt;
  
  
  It's there
&lt;/h3&gt;

&lt;p&gt;Are you ready?&lt;br&gt;
Are you really?&lt;br&gt;
Are you really really- alright, now I want you to do this.&lt;/p&gt;

&lt;p&gt;From the &lt;em&gt;local computer&lt;/em&gt; type the following and hit &lt;code&gt;enter&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh root@server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We did it! &lt;br&gt;
We finally get it to work!&lt;/p&gt;

&lt;h2&gt;
  
  
  Last Words
&lt;/h2&gt;

&lt;p&gt;Remember, the journey of a thousand miles begins with a single step. Take that step today.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
