<?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: Kaushik Mitra</title>
    <description>The latest articles on DEV Community by Kaushik Mitra (@mitrakumar).</description>
    <link>https://dev.to/mitrakumar</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F308572%2F5187b4fc-77b6-478c-93dc-2b5970ada150.jpeg</url>
      <title>DEV Community: Kaushik Mitra</title>
      <link>https://dev.to/mitrakumar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mitrakumar"/>
    <language>en</language>
    <item>
      <title>Creating a GKE Cluster and Deploying a Microservice Application (End-to-End)</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sat, 03 Oct 2026 22:41:59 +0000</pubDate>
      <link>https://dev.to/mitrakumar/creating-a-gke-cluster-and-deploying-a-microservice-application-end-to-end-2e2p</link>
      <guid>https://dev.to/mitrakumar/creating-a-gke-cluster-and-deploying-a-microservice-application-end-to-end-2e2p</guid>
      <description>&lt;p&gt;We've reached the final milestone of our Kubernetes learning journey!&lt;/p&gt;

&lt;p&gt;So far, we have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up a local Kubernetes cluster using &lt;strong&gt;Docker Desktop&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Built two microservices (&lt;code&gt;add-service&lt;/code&gt; and &lt;code&gt;multiply-service&lt;/code&gt;) and wired them together using &lt;strong&gt;Namespaces&lt;/strong&gt;, &lt;strong&gt;Pods&lt;/strong&gt;, and &lt;strong&gt;ClusterIP Services&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Configured &lt;strong&gt;NGINX Ingress Controller&lt;/strong&gt; for path-based routing (&lt;code&gt;/api/add&lt;/code&gt; and &lt;code&gt;/api/multiply&lt;/code&gt;) with URL rewriting.&lt;/li&gt;
&lt;li&gt;Pushed our container images to &lt;strong&gt;Google Cloud Artifact Registry&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, it's time to bring everything together into production: we'll provision a real &lt;strong&gt;Google Kubernetes Engine (GKE)&lt;/strong&gt; cluster on Google Cloud, deploy our application, configure cloud ingress with a public Google Cloud Load Balancer, and test our live API on the internet!&lt;/p&gt;




&lt;h2&gt;
  
  
  Why GKE Autopilot?
&lt;/h2&gt;

&lt;p&gt;Google Kubernetes Engine offers two operation modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Standard Mode&lt;/strong&gt;: You manage and pay for the underlying virtual machines (Compute Engine worker nodes). You handle node provisioning, OS patching, and node pool configurations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autopilot Mode&lt;/strong&gt;: Google manages the entire underlying node infrastructure, node pools, OS upgrades, and security hardening. You only define what your workloads need, and you pay exclusively for the CPU, memory, and storage requested by your actively running Pods.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For modern applications and learning environments, &lt;strong&gt;Autopilot&lt;/strong&gt; is fantastic because it eliminates node management overhead completely while enforcing Kubernetes production best practices out of the box.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GCP account and project (e.g., &lt;code&gt;kkm-kube-practice&lt;/code&gt;) with billing enabled.&lt;/li&gt;
&lt;li&gt;Container images already pushed to Artifact Registry (&lt;code&gt;asia-south1-docker.pkg.dev/...&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gcloud&lt;/code&gt; and &lt;code&gt;kubectl&lt;/code&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;The Kubernetes manifests and source code available on GitHub: &lt;a href="https://github.com/MitraKumar/kube-prac-calculator-app" rel="noopener noreferrer"&gt;MitraKumar/kube-prac-calculator-app&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Enable the Kubernetes Engine API
&lt;/h2&gt;

&lt;p&gt;Before creating a cluster, ensure the GKE API is active on your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;container.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Provision the GKE Autopilot Cluster
&lt;/h2&gt;

&lt;p&gt;Let's create an Autopilot cluster named &lt;code&gt;autopilot-cluster-1&lt;/code&gt; in the &lt;code&gt;us-central1&lt;/code&gt; region:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters create-auto autopilot-cluster-1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--project&lt;/span&gt; kkm-kube-practice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Cluster provisioning usually takes 4 to 7 minutes as Google provisions the regional control planes and private VPC networking. Sit back, grab a coffee, and let Google do the heavy lifting!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once creation finishes, you'll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                 LOCATION     MASTER_VERSION      STATUS
autopilot-cluster-1  us-central1  1.31.x-gke.xxx      RUNNING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Connect &lt;code&gt;kubectl&lt;/code&gt; to Your GKE Cluster
&lt;/h2&gt;

&lt;p&gt;To instruct your local &lt;code&gt;kubectl&lt;/code&gt; to talk to your brand new GKE cluster, download the cluster credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials autopilot-cluster-1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--project&lt;/span&gt; kkm-kube-practice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify your active context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl config current-context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gke_kkm-kube-practice_us-central1_autopilot-cluster-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's inspect the nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the initial worker nodes provisioned by Autopilot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Important Autopilot Requirement — Resource Requests
&lt;/h2&gt;

&lt;p&gt;One key difference between local Docker Desktop and GKE Autopilot is that &lt;strong&gt;Autopilot requires every container to declare CPU and memory requests and limits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because Autopilot scales nodes dynamically based on workload demands and bills based on pod resource consumption, it needs to know your pod's requirements.&lt;/p&gt;

&lt;p&gt;If you inspect our Pod manifests (&lt;code&gt;k8s/01-add-service-pod.yaml&lt;/code&gt; and &lt;code&gt;k8s/03-multiply-service-pod.yaml&lt;/code&gt;), you'll see we explicitly declared:&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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; If you deploy a pod without &lt;code&gt;resources&lt;/code&gt; on GKE Autopilot, Autopilot will automatically inject default resource values, but it's best practice to specify them explicitly.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 5: Install NGINX Ingress Controller on GKE using Helm
&lt;/h2&gt;

&lt;p&gt;Just like we did locally, we need an Ingress Controller to route external HTTP traffic to our services. We'll install the official chart using &lt;strong&gt;Helm&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="c"&gt;# 1. Add and update the official repository&lt;/span&gt;
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

&lt;span class="c"&gt;# 2. Install the NGINX Ingress Controller chart into GKE&lt;/span&gt;
helm &lt;span class="nb"&gt;install &lt;/span&gt;ingress-nginx ingress-nginx/ingress-nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; ingress-nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME: ingress-nginx
LAST DEPLOYED: Sat Oct  3 20:08:22 2026
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What happens on Google Cloud?
&lt;/h3&gt;

&lt;p&gt;When Helm installs this chart on GKE:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GKE Autopilot schedules the &lt;code&gt;ingress-nginx-controller&lt;/code&gt; pod and allocates compute resources automatically.&lt;/li&gt;
&lt;li&gt;GKE detects the &lt;code&gt;type: LoadBalancer&lt;/code&gt; Service in the &lt;code&gt;ingress-nginx&lt;/code&gt; namespace.&lt;/li&gt;
&lt;li&gt;Google Cloud automatically provisions a &lt;strong&gt;Google Cloud Network Load Balancer (NLB)&lt;/strong&gt; in your GCP project!&lt;/li&gt;
&lt;li&gt;Google assigns a public, routable IPv4 address to that Load Balancer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Watch the service until the public &lt;code&gt;EXTERNAL-IP&lt;/code&gt; is assigned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc &lt;span class="nt"&gt;-n&lt;/span&gt; ingress-nginx &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initial output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)
ingress-nginx-controller   LoadBalancer   34.118.235.60   &amp;lt;pending&amp;gt;     80:32234/TCP,443:32055/TCP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After about 1 to 2 minutes, the external IP will appear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)
ingress-nginx-controller   LoadBalancer   34.118.235.60   136.80.62.73    80:32234/TCP,443:32055/TCP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a dedicated public IP (&lt;code&gt;136.80.62.73&lt;/code&gt;) routing internet traffic directly into our cluster!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Deploy the Calculator Microservices
&lt;/h2&gt;

&lt;p&gt;Now, let's deploy our manifests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Namespace&lt;/strong&gt;: &lt;code&gt;00-namespace.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Service Pod &amp;amp; Service&lt;/strong&gt;: &lt;code&gt;01-add-service-pod.yaml&lt;/code&gt;, &lt;code&gt;02-add-service-cluster-ip.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiply Service Pod &amp;amp; Service&lt;/strong&gt;: &lt;code&gt;03-multiply-service-pod.yaml&lt;/code&gt;, &lt;code&gt;04-multiply-service-cluster-ip.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs Service Pod &amp;amp; Service&lt;/strong&gt;: &lt;code&gt;06-docs-service-pod.yaml&lt;/code&gt;, &lt;code&gt;07-docs-service-cluster-ip.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ingress Routing Rules&lt;/strong&gt;: &lt;code&gt;05-nginx-ingress-controller.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Apply them sequentially (or all together):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/00-namespace.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/01-add-service-pod.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/02-add-service-cluster-ip.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/03-multiply-service-pod.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/04-multiply-service-cluster-ip.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/06-docs-service-pod.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/07-docs-service-cluster-ip.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/05-nginx-ingress-controller.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify the Deployments
&lt;/h3&gt;

&lt;p&gt;Check the pods and services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                       READY   STATUS    RESTARTS   AGE
pod/add-service            1/1     Running   0          2m
pod/docs-service           1/1     Running   0          1m
pod/multiply-service       1/1     Running   0          2m

NAME                                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/add-service-cluster-ip        ClusterIP   34.118.228.222   &amp;lt;none&amp;gt;        3000/TCP   2m
service/docs-service-cluster-ip       ClusterIP   34.118.228.50    &amp;lt;none&amp;gt;        3000/TCP   1m
service/multiply-service-cluster-ip   ClusterIP   34.118.230.113   &amp;lt;none&amp;gt;        3000/TCP   2m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the Ingress resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get ingress &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                     CLASS   HOSTS   ADDRESS        PORTS   AGE
calculator-api-ingress   nginx   *       136.80.62.73   80      2m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ingress is active and bound to our Google Cloud Load Balancer IP (&lt;code&gt;136.80.62.73&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Test Your Live Production Application!
&lt;/h2&gt;

&lt;p&gt;Let's test our live microservices from anywhere in the world using &lt;code&gt;curl&lt;/code&gt; (or directly from your browser):&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Test Interactive Swagger UI Documentation:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; &lt;span class="s2"&gt;"http://136.80.62.73/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🌐 Open &lt;strong&gt;&lt;code&gt;http://136.80.62.73/&lt;/code&gt;&lt;/strong&gt; in your browser to explore the interactive Swagger documentation and test API endpoints right from the UI!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Test Addition API:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://136.80.62.73/api/add?a=10&amp;amp;b=20"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"addition"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Test Multiplication API:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://136.80.62.73/api/multiply?a=5&amp;amp;b=6"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"multiply-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"multiplication"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Test Health Check:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://136.80.62.73/api/add/health"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add-service"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! The Google Cloud Load Balancer receives the HTTP request on port 80, directs it to the NGINX Ingress Controller, which rewrites the path and dispatches it over the internal cluster network to our respective Node.js pods.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Clean Up Resources (Don't Skip This!)
&lt;/h2&gt;

&lt;p&gt;Cloud resources cost money when left running. If you are experimenting or practicing, remember to tear down the cluster once you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters delete autopilot-cluster-1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--project&lt;/span&gt; kkm-kube-practice &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--quiet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deleting the GKE cluster will automatically clean up the associated Google Cloud Network Load Balancer and internal VPC forwarding rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion &amp;amp; Series Retrospective
&lt;/h2&gt;

&lt;p&gt;Over this 5-part journey, we built a complete end-to-end Kubernetes workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local Setup&lt;/strong&gt;: Bootstrapped a frictionless single-node cluster using Docker Desktop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Core&lt;/strong&gt;: Built multi-service Node.js Express apps and mastered Namespaces, Pods, ClusterIP Services, Labels, and Selectors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ingress Routing&lt;/strong&gt;: Configured the NGINX Ingress Controller to route path-based traffic with regex rewrites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifact Management&lt;/strong&gt;: Tagged and published production containers to Google Cloud Artifact Registry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Deployment&lt;/strong&gt;: Created a managed GKE Autopilot cluster, provisioned a Cloud Load Balancer, and served live API traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope this step-by-step guide helps you navigate Kubernetes and cloud deployments with confidence.&lt;/p&gt;

&lt;p&gt;If you followed along or have questions about GKE, Ingress, or microservices, drop a comment below—I'd love to hear your thoughts!&lt;/p&gt;

</description>
      <category>gcp</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Pushing Container Images to Google Cloud Artifact Registry (Step-by-Step)</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sat, 03 Oct 2026 22:40:54 +0000</pubDate>
      <link>https://dev.to/mitrakumar/pushing-container-images-to-google-cloud-artifact-registry-step-by-step-3h0o</link>
      <guid>https://dev.to/mitrakumar/pushing-container-images-to-google-cloud-artifact-registry-step-by-step-3h0o</guid>
      <description>&lt;p&gt;When running Kubernetes locally with Docker Desktop, you have a handy superpower: your local Kubernetes cluster shares your computer's local Docker daemon. Any image you build via &lt;code&gt;docker build&lt;/code&gt; is immediately available to your local pods without needing to push it to the internet.&lt;/p&gt;

&lt;p&gt;However, the moment you decide to deploy to a managed cloud cluster like &lt;strong&gt;Google Kubernetes Engine (GKE)&lt;/strong&gt;, that magic ends. GKE nodes run in Google Cloud's data centers and have no access to your laptop's disk. They need a secure, centralized container registry from which to pull your container images.&lt;/p&gt;

&lt;p&gt;Google Container Registry (&lt;code&gt;gcr.io&lt;/code&gt;) has now been deprecated in favor of &lt;strong&gt;Google Cloud Artifact Registry&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In this guide, we'll walk through setting up Google Cloud Artifact Registry, configuring Docker authentication, tagging our images, and pushing our microservices to Google Cloud.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;Google Cloud Platform (GCP)&lt;/strong&gt; account with an active project (e.g., &lt;code&gt;kkm-kube-practice&lt;/code&gt;) and billing enabled.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;Google Cloud SDK (&lt;code&gt;gcloud&lt;/code&gt; CLI)&lt;/a&gt;&lt;/strong&gt; installed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; installed and running on your machine.&lt;/li&gt;
&lt;li&gt;The sample microservices and Dockerfiles from our GitHub repository: &lt;a href="https://github.com/MitraKumar/kube-prac-calculator-app" rel="noopener noreferrer"&gt;MitraKumar/kube-prac-calculator-app&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Authenticate and Set Up Your GCP Project
&lt;/h2&gt;

&lt;p&gt;First, let's log into GCP from our terminal and set our active project.&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="c"&gt;# Authenticate your Google account&lt;/span&gt;
gcloud auth login

&lt;span class="c"&gt;# Set your active project ID&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project kkm-kube-practice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Replace &lt;code&gt;kkm-kube-practice&lt;/code&gt; with your own GCP Project ID throughout this tutorial!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Enable the Artifact Registry API
&lt;/h3&gt;

&lt;p&gt;Before we can create a repository, we must enable the Artifact Registry API on our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;artifactregistry.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Create a Docker Repository in Artifact Registry
&lt;/h2&gt;

&lt;p&gt;Google Cloud Artifact Registry supports multiple package types (Docker, npm, Maven, Python, etc.). We need a &lt;strong&gt;Docker&lt;/strong&gt; repository.&lt;/p&gt;

&lt;p&gt;We will create a repository named &lt;code&gt;kkm-dock&lt;/code&gt; located in the &lt;code&gt;asia-south1&lt;/code&gt; region (Mumbai). You can choose any region closest to you or your future GKE cluster (e.g., &lt;code&gt;us-central1&lt;/code&gt;, &lt;code&gt;europe-west1&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;gcloud artifacts repositories create kkm-dock &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--repository-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;asia-south1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Docker repository for K8s practice microservices"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that the repository was created successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud artifacts repositories list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY  FORMAT  MODE                 LOCATION     DESCRIPTION
kkm-dock    DOCKER  STANDARD_REPOSITORY  asia-south1  Docker repository for K8s practice microservices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Configure Docker Authentication
&lt;/h2&gt;

&lt;p&gt;In order for your standard &lt;code&gt;docker push&lt;/code&gt; and &lt;code&gt;docker pull&lt;/code&gt; commands to securely authenticate against Google Cloud, Docker needs to know how to obtain GCP access tokens.&lt;/p&gt;

&lt;p&gt;Google provides a credential helper integrated right into &lt;code&gt;gcloud&lt;/code&gt;. Run 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;gcloud auth configure-docker asia-south1-docker.pkg.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted, confirm with &lt;code&gt;Y&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What did this command just do? It updated your local &lt;code&gt;~/.docker/config.json&lt;/code&gt; file so that any time Docker interacts with &lt;code&gt;asia-south1-docker.pkg.dev&lt;/code&gt;, it automatically retrieves an authentication token using your active &lt;code&gt;gcloud&lt;/code&gt; session. No hardcoded passwords or long-lived API keys on your machine!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Understand the Artifact Registry Image Naming Structure
&lt;/h2&gt;

&lt;p&gt;Google Cloud Artifact Registry requires a specific image naming format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[REGION]-docker.pkg.dev/[PROJECT_ID]/[REPOSITORY_NAME]/[IMAGE_NAME]:[TAG]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down our path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Region Host&lt;/strong&gt;: &lt;code&gt;asia-south1-docker.pkg.dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project ID&lt;/strong&gt;: &lt;code&gt;kkm-kube-practice&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt;: &lt;code&gt;kkm-dock&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image Name&lt;/strong&gt;: &lt;code&gt;add-service&lt;/code&gt;, &lt;code&gt;multiply-service&lt;/code&gt;, and &lt;code&gt;docs-service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag&lt;/strong&gt;: &lt;code&gt;1.0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Putting it all together gives our fully qualified image URLs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/add-service:1.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/multiply-service:1.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/docs-service:1.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Tag Your Local Images
&lt;/h2&gt;

&lt;p&gt;Now, let's tag our existing local images with the Artifact Registry URL.&lt;/p&gt;

&lt;p&gt;If you already built &lt;code&gt;add-service:1.0&lt;/code&gt;, &lt;code&gt;multiply-service:1.0&lt;/code&gt;, and &lt;code&gt;docs-service:1.0&lt;/code&gt; locally, you can tag them directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag add-service:1.0 &lt;span class="se"&gt;\&lt;/span&gt;
  asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/add-service:1.0

docker tag multiply-service:1.0 &lt;span class="se"&gt;\&lt;/span&gt;
  asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/multiply-service:1.0

docker tag docs-service:1.0 &lt;span class="se"&gt;\&lt;/span&gt;
  asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/docs-service:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify with &lt;code&gt;docker images&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;docker images | &lt;span class="nb"&gt;grep &lt;/span&gt;pkg.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY                                                               TAG   IMAGE ID       SIZE
asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/add-service        1.0   420017c8b88f   49.9MB
asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/docs-service       1.0   517e75577f70   60.4MB
asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/multiply-service   1.0   c91c40363892   49.9MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Push the Images to Artifact Registry
&lt;/h2&gt;

&lt;p&gt;Now comes the exciting moment—pushing your images to the cloud!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/add-service:1.0
docker push asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/multiply-service:1.0
docker push asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/docs-service:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 7: Verify the Uploaded Images
&lt;/h2&gt;

&lt;p&gt;You can list the images stored in your repository directly from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud artifacts docker images list asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMAGE                                                                    TAGS  DIGEST        CREATE_TIME
asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/add-service        1.0   sha256:...   2026-10-03
asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/docs-service       1.0   sha256:...   2026-10-03
asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/multiply-service   1.0   sha256:...   2026-10-03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also open the &lt;strong&gt;Google Cloud Console&lt;/strong&gt;, navigate to &lt;strong&gt;Artifact Registry -&amp;gt; Repositories -&amp;gt; kkm-dock&lt;/strong&gt;, and inspect your uploaded image layers, vulnerabilities, and tags visually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Update Your Kubernetes Manifests
&lt;/h2&gt;

&lt;p&gt;Now that our images are hosted in Artifact Registry, we update the &lt;code&gt;image:&lt;/code&gt; fields in our Kubernetes manifests so GKE knows where to pull them from.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;k8s/01-add-service-pod.yaml&lt;/code&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="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-service&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;asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/add-service:1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;k8s/03-multiply-service-pod.yaml&lt;/code&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="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;multiply-service&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;asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/multiply-service:1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in &lt;code&gt;k8s/06-docs-service-pod.yaml&lt;/code&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="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs-service&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;asia-south1-docker.pkg.dev/kkm-kube-practice/kkm-dock/docs-service:1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on Permissions:&lt;/strong&gt; When running on GKE within the same GCP project, GKE default service accounts automatically have read/pull permissions to Artifact Registry repositories in the same project! No complex imagePullSecrets required.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Conclusion &amp;amp; Next Step
&lt;/h2&gt;

&lt;p&gt;Our microservice container images are now safely built, tagged, and published to Google Cloud Artifact Registry.&lt;/p&gt;

&lt;p&gt;In the final post of this series, we will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spin up a managed &lt;strong&gt;Google Kubernetes Engine (GKE Autopilot)&lt;/strong&gt; cluster.&lt;/li&gt;
&lt;li&gt;Install the NGINX Ingress controller on GKE.&lt;/li&gt;
&lt;li&gt;Deploy our calculator application to production and test it using a real public Cloud Load Balancer IP!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stay tuned, and drop any questions in the comments below!&lt;/p&gt;

</description>
      <category>gcp</category>
      <category>kubernetes</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Installing NGINX Ingress Controller in Docker Desktop: A Step-by-Step Guide</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sat, 03 Oct 2026 22:39:58 +0000</pubDate>
      <link>https://dev.to/mitrakumar/installing-nginx-ingress-controller-in-docker-desktop-a-step-by-step-guide-17df</link>
      <guid>https://dev.to/mitrakumar/installing-nginx-ingress-controller-in-docker-desktop-a-step-by-step-guide-17df</guid>
      <description>&lt;p&gt;In our previous post, we deployed three microservices (&lt;code&gt;add-service&lt;/code&gt;, &lt;code&gt;multiply-service&lt;/code&gt;, and &lt;code&gt;docs-service&lt;/code&gt;) and exposed them using Kubernetes &lt;code&gt;ClusterIP&lt;/code&gt; services. While everything was running smoothly, there was one major limitation: &lt;code&gt;ClusterIP&lt;/code&gt; services are internal to the cluster. To access them, we had to rely on &lt;code&gt;kubectl port-forward&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In a real-world scenario, you want a single entry point (usually port 80 or 443) that acts as an intelligent reverse proxy, routing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests for &lt;code&gt;/&lt;/code&gt; to the &lt;code&gt;docs-service&lt;/code&gt; (Swagger/OpenAPI documentation)&lt;/li&gt;
&lt;li&gt;Requests for &lt;code&gt;/api/add&lt;/code&gt; to the &lt;code&gt;add-service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Requests for &lt;code&gt;/api/multiply&lt;/code&gt; to the &lt;code&gt;multiply-service&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've read my previous article on &lt;a href="https://dev.to/mitrakumar/magic-behind-ddev-lando-routing-37m"&gt;how DDEV and Lando use Traefik for routing&lt;/a&gt;, this concept will feel very familiar. In Kubernetes, this reverse-proxy layer is handled by an &lt;strong&gt;Ingress Controller&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this guide, we'll install the official &lt;strong&gt;NGINX Ingress Controller&lt;/strong&gt; in Docker Desktop and configure path-based routing with URL rewrites.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;Source Code:&lt;/strong&gt; All the manifests, Dockerfiles, and architecture diagrams used in this post are available on GitHub: &lt;a href="https://github.com/MitraKumar/kube-prac-calculator-app" rel="noopener noreferrer"&gt;MitraKumar/kube-prac-calculator-app&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Ingress Resource vs. Ingress Controller
&lt;/h2&gt;

&lt;p&gt;Before we run any commands, let's clarify an important distinction that trips up many beginners:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingress Resource (&lt;code&gt;kind: Ingress&lt;/code&gt;)&lt;/strong&gt;: This is just a set of routing rules written in YAML (e.g., "forward &lt;code&gt;/api/add&lt;/code&gt; to service A"). By itself, this YAML file does nothing!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ingress Controller&lt;/strong&gt;: This is the actual software engine (like NGINX, Traefik, or HAProxy) running inside your cluster. It watches for Ingress resources, reads the rules, and automatically configures its internal reverse-proxy routing tables.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To route traffic, you &lt;strong&gt;must&lt;/strong&gt; have an Ingress Controller running.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Install NGINX Ingress Controller using Helm
&lt;/h2&gt;

&lt;p&gt;While you can install the Ingress Controller using static YAML manifests, the recommended and industry-standard way to manage it is using &lt;strong&gt;Helm&lt;/strong&gt; (the package manager for Kubernetes). &lt;/p&gt;

&lt;p&gt;Using Helm makes upgrading, modifying configurations, and uninstalling as simple as running a single command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Don't have Helm installed?&lt;/strong&gt; You can install Helm in seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: &lt;code&gt;brew install helm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ubuntu/Debian&lt;/strong&gt;: &lt;code&gt;sudo snap install helm --classic&lt;/code&gt; or &lt;code&gt;sudo apt-get install helm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: &lt;code&gt;winget install Helm.Helm&lt;/code&gt; or &lt;code&gt;choco install kubernetes-helm&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1. Add the Ingress-NGINX Helm Repository
&lt;/h3&gt;

&lt;p&gt;First, add the official NGINX Ingress repository to Helm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update your local Helm repository cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Install the Helm Chart
&lt;/h3&gt;

&lt;p&gt;Now, install the chart into its own dedicated namespace (&lt;code&gt;ingress-nginx&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;helm &lt;span class="nb"&gt;install &lt;/span&gt;ingress-nginx ingress-nginx/ingress-nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; ingress-nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME: ingress-nginx
LAST DEPLOYED: Sat Oct  3 20:08:22 2026
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What did Helm just create?
&lt;/h3&gt;

&lt;p&gt;By default, the Helm chart creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ingress-nginx&lt;/code&gt; namespace.&lt;/li&gt;
&lt;li&gt;The NGINX Ingress Controller deployment and replica pods.&lt;/li&gt;
&lt;li&gt;RBAC roles, cluster roles, service accounts, and admission webhooks.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;LoadBalancer&lt;/code&gt; service that Docker Desktop automatically binds to ports &lt;strong&gt;80&lt;/strong&gt; and &lt;strong&gt;443&lt;/strong&gt; on &lt;code&gt;localhost&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Verify the Controller is Running
&lt;/h2&gt;

&lt;p&gt;Let's monitor the deployment and wait until the controller pod is in the &lt;code&gt;Running&lt;/code&gt; state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--namespace&lt;/span&gt; ingress-nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ready pod &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--selector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;app.kubernetes.io/component&lt;span class="o"&gt;=&lt;/span&gt;controller &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;120s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the pods in the &lt;code&gt;ingress-nginx&lt;/code&gt; namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; ingress-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-controller-7c444fc6cf-djw7s   1/1     Running     0          45s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's check the Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc &lt;span class="nt"&gt;-n&lt;/span&gt; ingress-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.96.231.158   localhost     80:30905/TCP,443:30913/TCP   60s
ingress-nginx-controller-admission   ClusterIP      10.96.233.245   &amp;lt;none&amp;gt;        443/TCP                      60s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; The &lt;code&gt;EXTERNAL-IP&lt;/code&gt; is listed as &lt;code&gt;localhost&lt;/code&gt; (or an internal bridge IP like &lt;code&gt;172.18.0.x&lt;/code&gt;). This means traffic sent to &lt;code&gt;http://localhost:80&lt;/code&gt; will now be routed directly to the NGINX Ingress Controller!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Create the Ingress Rules (&lt;code&gt;05-nginx-ingress-controller.yaml&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Now that the controller is listening, let's give it rules to route our calculator microservices.&lt;/p&gt;

&lt;p&gt;Here is &lt;code&gt;k8s/05-nginx-ingress-controller.yaml&lt;/code&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-api-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-app&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/use-regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/$2&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api/add(/|$)(.*)&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImplementationSpecific&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-service-cluster-ip&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api/multiply(/|$)(.*)&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImplementationSpecific&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;multiply-service-cluster-ip&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/()(.*)&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImplementationSpecific&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs-service-cluster-ip&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breaking Down the Configuration
&lt;/h3&gt;

&lt;p&gt;Let's dissect the important parts of this manifest:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ingressClassName: nginx&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
Tells Kubernetes that this Ingress resource should be managed by the NGINX Ingress Controller we just installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;nginx.ingress.kubernetes.io/use-regex: "true"&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
Enables regular expression matching on the paths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Path Matching &amp;amp; Rewrite Target (&lt;code&gt;/$2&lt;/code&gt;)&lt;/strong&gt;:&lt;br&gt;
Our microservices are simple Express apps that listen for requests on the root path &lt;code&gt;/&lt;/code&gt; (e.g., &lt;code&gt;/?a=10&amp;amp;b=20&lt;/code&gt;).&lt;br&gt;
However, our public URL is &lt;code&gt;/api/add?a=10&amp;amp;b=20&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If NGINX forwarded &lt;code&gt;/api/add&lt;/code&gt; directly to the container, Express would return a &lt;code&gt;404 Not Found&lt;/code&gt; because it doesn't have an &lt;code&gt;/api/add&lt;/code&gt; route!&lt;/p&gt;

&lt;p&gt;Here is where regex capture groups save the day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/add(/|$)(.*)&lt;/code&gt; matches the addition route. Group 1 is &lt;code&gt;(/|$)&lt;/code&gt;, Group 2 is &lt;code&gt;(.*)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/multiply(/|$)(.*)&lt;/code&gt; matches the multiplication route.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rewrite-target: /$2&lt;/code&gt;: NGINX strips the prefix &lt;code&gt;/api/add&lt;/code&gt; and rewrites the incoming request to &lt;code&gt;/$2&lt;/code&gt; before forwarding it to our Node.js pod!&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Serving Swagger Documentation at Root (&lt;code&gt;/&lt;/code&gt; via &lt;code&gt;/()(.*)&lt;/code&gt;)&lt;/strong&gt;:
For our &lt;code&gt;docs-service&lt;/code&gt;, we match &lt;code&gt;/()(.*)&lt;/code&gt;. 

&lt;ul&gt;
&lt;li&gt;Group 1 is empty &lt;code&gt;()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Group 2 &lt;code&gt;(.*)&lt;/code&gt; captures the rest of the path (e.g. &lt;code&gt;/&lt;/code&gt; becomes &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/swagger-ui.css&lt;/code&gt; becomes &lt;code&gt;/swagger-ui.css&lt;/code&gt;, &lt;code&gt;/openapi.json&lt;/code&gt; becomes &lt;code&gt;/openapi.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;NGINX matches the longer paths (&lt;code&gt;/api/add&lt;/code&gt; and &lt;code&gt;/api/multiply&lt;/code&gt;) first, and falls back to our Swagger UI documentation for all root and documentation requests!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 4: Apply the Ingress and Test on Localhost
&lt;/h2&gt;

&lt;p&gt;Let's apply our Ingress manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/05-nginx-ingress-controller.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that the Ingress resource was created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get ingress &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                     CLASS   HOSTS   ADDRESS     PORTS   AGE
calculator-api-ingress   nginx   *       localhost   80      10s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing the Endpoints!
&lt;/h3&gt;

&lt;p&gt;No port-forwarding needed anymore! We can query &lt;code&gt;http://localhost&lt;/code&gt; directly on port 80:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Test Swagger UI Documentation (Root Route &lt;code&gt;/&lt;/code&gt;):
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🌐 You can now open &lt;strong&gt;&lt;code&gt;http://localhost/&lt;/code&gt;&lt;/strong&gt; directly in your browser to view and interact with the Swagger UI documentation!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  2. Test Addition Service:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost/api/add?a=10&amp;amp;b=20"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"addition"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Test Multiplication Service:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost/api/multiply?a=5&amp;amp;b=6"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"multiply-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"multiplication"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Test Health Checks:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost/api/add/health"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add-service"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three services are working seamlessly through a single local entry point on standard port 80!&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting Common Gotchas
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Port 80 Already in Use on Host&lt;/strong&gt;:
If the ingress controller pod stays in &lt;code&gt;Pending&lt;/code&gt; or crashes with port binding errors, another application on your machine (Apache, system Nginx, IIS, Skype, or local dev tools) is already listening on port 80. Stop that service or inspect who is using port 80 with:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Getting a 404 from NGINX vs Node.js&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;If the response header contains &lt;code&gt;Server: openresty&lt;/code&gt; or &lt;code&gt;Server: nginx&lt;/code&gt; and returns HTML &lt;code&gt;404 Not Found&lt;/code&gt;, your Ingress path regex didn't match the URL.&lt;/li&gt;
&lt;li&gt;If the response returns JSON &lt;code&gt;Cannot GET /api/add&lt;/code&gt;, your &lt;code&gt;rewrite-target&lt;/code&gt; annotation is missing or misconfigured, and the un-rewritten path reached Express.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusion &amp;amp; Next Step
&lt;/h2&gt;

&lt;p&gt;We now have a complete, professional local Kubernetes environment running on Docker Desktop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-service backend in its own Namespace.&lt;/li&gt;
&lt;li&gt;Stable internal routing via ClusterIP Services.&lt;/li&gt;
&lt;li&gt;External path routing and URL rewrites via NGINX Ingress Controller.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our next big step is &lt;strong&gt;taking this to the cloud&lt;/strong&gt;. But before we can deploy to Google Kubernetes Engine (GKE), GKE needs a way to download our container images.&lt;/p&gt;

&lt;p&gt;In the next post, we will set up &lt;strong&gt;Google Cloud Artifact Registry&lt;/strong&gt;, configure Docker authentication, and push our microservice images to GCP!&lt;/p&gt;

&lt;p&gt;Leave a comment below if you ran into any regex or routing issues!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Hands-On Guide with Microservices</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sat, 03 Oct 2026 21:45:11 +0000</pubDate>
      <link>https://dev.to/mitrakumar/a-hands-on-guide-with-microservices-1o4n</link>
      <guid>https://dev.to/mitrakumar/a-hands-on-guide-with-microservices-1o4n</guid>
      <description>&lt;p&gt;When starting out with Kubernetes, terms like &lt;strong&gt;Pods&lt;/strong&gt;, &lt;strong&gt;Deployments&lt;/strong&gt;, &lt;strong&gt;Services&lt;/strong&gt;, and &lt;strong&gt;Namespaces&lt;/strong&gt; get thrown around constantly. Reading definitions in the documentation is one thing, but seeing how they actually connect and route traffic between multiple services is where the concepts truly click.&lt;/p&gt;

&lt;p&gt;Today, we're going to build and deploy a practical multi-service application: a &lt;strong&gt;Calculator App&lt;/strong&gt; composed of three independent microservices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;add-service&lt;/code&gt;&lt;/strong&gt;: An Express.js microservice handling addition requests (&lt;code&gt;:3001&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;multiply-service&lt;/code&gt;&lt;/strong&gt;: An Express.js microservice handling multiplication requests (&lt;code&gt;:3002&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docs-service&lt;/code&gt;&lt;/strong&gt;: An Express.js microservice serving interactive Swagger/OpenAPI documentation (&lt;code&gt;:3003&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will package them with Docker, create Kubernetes manifests, and understand how &lt;strong&gt;Pods&lt;/strong&gt; and &lt;strong&gt;ClusterIP Services&lt;/strong&gt; work together under the hood.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;Source Code:&lt;/strong&gt; You can find the full source code, Dockerfiles, and Kubernetes manifests for this project on GitHub: &lt;a href="https://github.com/MitraKumar/kube-prac-calculator-app" rel="noopener noreferrer"&gt;MitraKumar/kube-prac-calculator-app&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Application Architecture
&lt;/h2&gt;

&lt;p&gt;Before touching any Kubernetes YAML, let's understand what we are deploying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                          [Client Request]
                                 |
        +------------------------+------------------------+
        |                        |                        |
        v                        v                        v
+-----------------------+ +----------------------------+ +------------------------+
| add-service-cluster-ip| | multiply-service-cluster-ip| | docs-service-cluster-ip|
| Service (Port 3000)   | | Service (Port 3000)        | | Service (Port 3000)    |
+-----------------------+ +----------------------------+ +------------------------+
        |                                |                        |
        v                                v                        v
+-----------------------+ +----------------------------+ +------------------------+
| Pod: add-service      | | Pod: multiply-service      | | Pod: docs-service      |
| (:3001)               | | (:3002)                    | | (:3003 - Swagger UI)   |
+-----------------------+ +----------------------------+ +------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each service is a lightweight Node.js Express server.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;add-service&lt;/code&gt; (&lt;code&gt;add-service/index.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Listens on port &lt;strong&gt;3001&lt;/strong&gt; and handles addition via query parameters &lt;code&gt;?a=10&amp;amp;b=20&lt;/code&gt; or JSON body:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&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;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3001&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleAddition&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&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;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;a&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&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;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;b&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&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;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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please provide valid numbers for "a" and "b" via query params or JSON body.&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="k"&gt;return&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;addition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&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;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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add-service&lt;/span&gt;&lt;span class="dl"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleAddition&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="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Add Service is running on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;code&gt;multiply-service&lt;/code&gt; (&lt;code&gt;multiply-service/index.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Identical structure, but performs multiplication and listens on port &lt;strong&gt;3002&lt;/strong&gt;:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&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;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3002&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMultiplication&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&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;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;a&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&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;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;b&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&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;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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please provide valid numbers for "a" and "b" via query params or JSON body.&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="k"&gt;return&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiply-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiplication&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&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;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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiply-service&lt;/span&gt;&lt;span class="dl"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMultiplication&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="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Multiply Service is running on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;code&gt;docs-service&lt;/code&gt; (&lt;code&gt;docs-service/index.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Listens on port &lt;strong&gt;3003&lt;/strong&gt; and serves an interactive Swagger UI documentation at &lt;code&gt;/&lt;/code&gt; and OpenAPI specification at &lt;code&gt;/openapi.json&lt;/code&gt;:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&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;swaggerUi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;swagger-ui-express&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;swaggerDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./swagger.json&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;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3003&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Health check endpoint&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&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;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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docs-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// Raw OpenAPI JSON spec&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/openapi.json&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;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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;swaggerDocument&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Swagger UI at root route /&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;swaggerUi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serve&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;swaggerUi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;swaggerDocument&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="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Docs Service is running on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Dockerizing the Services
&lt;/h3&gt;

&lt;p&gt;All three services use a clean, lightweight Dockerfile based on Node Alpine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="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; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3001 # 3002 for multiply, 3003 for docs&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the local images with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; add-service:1.0 ./add-service
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; multiply-service:1.0 ./multiply-service
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docs-service:1.0 ./docs-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Diving into Kubernetes Concepts
&lt;/h2&gt;

&lt;p&gt;Now, let's deploy these microservices into our Kubernetes cluster. We organize all our manifests inside a &lt;code&gt;k8s/&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k8s/
├── 00-namespace.yaml
├── 01-add-service-pod.yaml
├── 02-add-service-cluster-ip.yaml
├── 03-multiply-service-pod.yaml
├── 04-multiply-service-cluster-ip.yaml
├── 06-docs-service-pod.yaml
└── 07-docs-service-cluster-ip.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Isolating with Namespaces (&lt;code&gt;00-namespace.yaml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Namespaces help you group related resources logically and prevent naming collisions across teams or environments.&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Namespace&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/00-namespace.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: What is a Pod? (&lt;code&gt;01-add-service-pod.yaml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;In Docker, the atomic unit is a &lt;strong&gt;container&lt;/strong&gt;. In Kubernetes, the smallest deployable unit is a &lt;strong&gt;Pod&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A Pod is a wrapper around one or more tightly coupled containers that share the same network namespace (IP address and port space) and storage volumes.&lt;/p&gt;

&lt;p&gt;Here is our Pod manifest for &lt;code&gt;add-service&lt;/code&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-service&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-app&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-service&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;add-service:1.0&lt;/span&gt;
    &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
      &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;metadata.labels&lt;/code&gt;: We attach a label &lt;code&gt;app.kubernetes.io/name: add-service&lt;/code&gt;. This label acts as a metadata tag that our Service will look for.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resources.requests &amp;amp; limits&lt;/code&gt;: Specifies the minimum and maximum CPU/RAM the pod can consume.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;containerPort: 3001&lt;/code&gt;: The port on which our Node.js app inside the container listens.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Likewise, &lt;code&gt;03-multiply-service-pod.yaml&lt;/code&gt; defines the multiply Pod (containerPort &lt;code&gt;3002&lt;/code&gt;) and &lt;code&gt;06-docs-service-pod.yaml&lt;/code&gt; defines the Swagger UI docs Pod (containerPort &lt;code&gt;3003&lt;/code&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs-service&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-app&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs-service&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;docs-service:1.0&lt;/span&gt;
    &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
      &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3003&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply all three pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/01-add-service-pod.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/03-multiply-service-pod.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/06-docs-service-pod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME               READY   STATUS    RESTARTS   AGE
add-service        1/1     Running   0          25s
docs-service       1/1     Running   0          18s
multiply-service   1/1     Running   0          20s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 3: Why Do We Need Services?
&lt;/h3&gt;

&lt;p&gt;Here is the fundamental challenge with Pods in Kubernetes: &lt;strong&gt;Pods are ephemeral&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pods get created, crash, get restarted, or get scheduled onto different nodes. Every time a Pod restarts, it receives a new internal IP address. If &lt;code&gt;multiply-service&lt;/code&gt; needs to talk to &lt;code&gt;add-service&lt;/code&gt;, it cannot rely on a hardcoded Pod IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter the Kubernetes Service.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Service provides a &lt;strong&gt;stable virtual IP (ClusterIP)&lt;/strong&gt; and a stable DNS name that never changes, even if underlying pods die and get replaced.&lt;/p&gt;

&lt;p&gt;Let's look at &lt;code&gt;k8s/02-add-service-cluster-ip.yaml&lt;/code&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-service-cluster-ip&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-service&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Secret Sauce: Labels and Selectors
&lt;/h4&gt;

&lt;p&gt;How does the Service know which Pod to send traffic to? &lt;/p&gt;

&lt;p&gt;Look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service &lt;code&gt;spec.selector&lt;/code&gt;: &lt;code&gt;app.kubernetes.io/name: add-service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pod &lt;code&gt;metadata.labels&lt;/code&gt;: &lt;code&gt;app.kubernetes.io/name: add-service&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Service continuously queries the Kubernetes API for any pods bearing that exact label and maintains an internal list of endpoints.&lt;/p&gt;

&lt;h4&gt;
  
  
  Port vs Target Port
&lt;/h4&gt;

&lt;p&gt;This often confuses beginners:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;port: 3000&lt;/code&gt;: The port that the &lt;strong&gt;Service itself&lt;/strong&gt; exposes inside the cluster. Other services will talk to &lt;code&gt;add-service-cluster-ip:3000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;targetPort: 3001&lt;/code&gt;: The port that the &lt;strong&gt;underlying Pod container&lt;/strong&gt; is actually listening on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kubernetes automatically routes incoming requests on port 3000 to port 3001 on the container!&lt;/p&gt;

&lt;p&gt;Now let's check &lt;code&gt;k8s/04-multiply-service-cluster-ip.yaml&lt;/code&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;multiply-service-cluster-ip&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;multiply-service&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3002&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how all three services expose port &lt;code&gt;3000&lt;/code&gt; to the internal cluster network, even though their underlying containers listen on &lt;code&gt;3001&lt;/code&gt;, &lt;code&gt;3002&lt;/code&gt;, and &lt;code&gt;3003&lt;/code&gt;. That is the elegance of Service abstraction.&lt;/p&gt;

&lt;p&gt;Here is &lt;code&gt;k8s/07-docs-service-cluster-ip.yaml&lt;/code&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs-service-cluster-ip&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calculator-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs-service&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3003&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply all three services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/02-add-service-cluster-ip.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/04-multiply-service-cluster-ip.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/07-docs-service-cluster-ip.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
add-service-cluster-ip        ClusterIP   10.96.220.14     &amp;lt;none&amp;gt;        3000/TCP   40s
docs-service-cluster-ip       ClusterIP   10.96.180.25     &amp;lt;none&amp;gt;        3000/TCP   15s
multiply-service-cluster-ip   ClusterIP   10.96.115.82     &amp;lt;none&amp;gt;        3000/TCP   35s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Testing Internal Connectivity
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;ClusterIP&lt;/code&gt; services are strictly internal, they do not have an external IP address (&lt;code&gt;EXTERNAL-IP: &amp;lt;none&amp;gt;&lt;/code&gt;). They cannot be directly reached from your browser yet.&lt;/p&gt;

&lt;p&gt;To verify that our Services are properly routing to our Pods, we can use &lt;code&gt;kubectl port-forward&lt;/code&gt; to tunnel traffic from our host machine into each Service:&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="c"&gt;# In Terminal 1: Forward add-service&lt;/span&gt;
kubectl port-forward svc/add-service-cluster-ip 8001:3000 &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal, test addition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"http://localhost:8001/?a=15&amp;amp;b=25"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"addition"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's test the multiplication service:&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="c"&gt;# In Terminal 2: Forward multiply-service&lt;/span&gt;
kubectl port-forward svc/multiply-service-cluster-ip 8002:3000 &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test multiplication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"http://localhost:8002/?a=6&amp;amp;b=7"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"multiply-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"multiplication"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, test the documentation service:&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="c"&gt;# In Terminal 3: Forward docs-service&lt;/span&gt;
kubectl port-forward svc/docs-service-cluster-ip 8003:3000 &lt;span class="nt"&gt;-n&lt;/span&gt; calculator-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test docs health check and Swagger HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:8003/health"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docs-service"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! Each Service successfully accepts traffic on port 3000 and maps it to the respective container ports (3001, 3002, and 3003).&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary &amp;amp; What's Missing?
&lt;/h2&gt;

&lt;p&gt;Here is what we achieved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Created a dedicated &lt;strong&gt;Namespace&lt;/strong&gt; (&lt;code&gt;calculator-app&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Created &lt;strong&gt;Pods&lt;/strong&gt; for three independent microservices with resource constraints.&lt;/li&gt;
&lt;li&gt;Created &lt;strong&gt;ClusterIP Services&lt;/strong&gt; that decouple network routing from ephemeral pod lifecycles using &lt;strong&gt;Labels &amp;amp; Selectors&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Big Question:&lt;/strong&gt;&lt;br&gt;
In a production app, we cannot ask users to run &lt;code&gt;kubectl port-forward&lt;/code&gt; to access our API, nor do we want to open multiple random ports for each service. We want a single entry point (e.g., &lt;code&gt;http://localhost/api/add&lt;/code&gt; and &lt;code&gt;http://localhost/api/multiply&lt;/code&gt;) on standard HTTP port 80.&lt;/p&gt;

&lt;p&gt;In the next post, we will install and configure the &lt;strong&gt;NGINX Ingress Controller&lt;/strong&gt; in Docker Desktop to achieve intelligent path-based routing.&lt;/p&gt;

&lt;p&gt;Have any questions about Labels, Selectors, or ClusterIP? Drop a comment below!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>node</category>
      <category>backend</category>
    </item>
    <item>
      <title>Setting up a Kubernetes Cluster Locally with Docker Desktop</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sat, 03 Oct 2026 21:43:25 +0000</pubDate>
      <link>https://dev.to/mitrakumar/setting-up-a-kubernetes-cluster-locally-with-docker-desktop-3lco</link>
      <guid>https://dev.to/mitrakumar/setting-up-a-kubernetes-cluster-locally-with-docker-desktop-3lco</guid>
      <description>&lt;p&gt;Recently I've started diving deeper into Kubernetes. If you are starting your cloud-native or container orchestration journey, the sheer number of tools out there can feel overwhelming—Minikube, Kind, K3s, MicroK8s, and cloud-managed options. &lt;/p&gt;

&lt;p&gt;When you just want to get your hands dirty without spending money on cloud resources or configuring complex virtual machines, &lt;strong&gt;Docker Desktop&lt;/strong&gt; offers the simplest starting point.&lt;/p&gt;

&lt;p&gt;Did you know that Docker Desktop comes with a fully functional single-node Kubernetes cluster built right into it? With a single checkbox, you can spin up a control plane and node right on your machine!&lt;/p&gt;

&lt;p&gt;In this guide, we'll walk through enabling Kubernetes in Docker Desktop, verifying your environment with &lt;code&gt;kubectl&lt;/code&gt;, and testing our local cluster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Docker Desktop for Local Kubernetes?
&lt;/h2&gt;

&lt;p&gt;For beginners and everyday local development, Docker Desktop has a few distinct advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero Extra Virtualization Setup&lt;/strong&gt;: If you already use Docker Desktop on macOS, Windows (via WSL2), or Linux, you don't need to install VirtualBox, Hyper-V extras, or special network bridges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Docker Daemon&lt;/strong&gt;: Container images built locally via &lt;code&gt;docker build&lt;/code&gt; are instantly accessible to your local Kubernetes cluster without needing to push to Docker Hub or a remote registry first!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Friction&lt;/strong&gt;: Starting, stopping, or resetting your entire cluster takes just one click from the UI.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt; installed and running.&lt;/li&gt;
&lt;li&gt;A terminal (Bash, Zsh, or PowerShell).&lt;/li&gt;
&lt;li&gt;Recommended: At least 4GB to 8GB of RAM allocated to Docker Desktop (check Settings -&amp;gt; Resources).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;Source Code:&lt;/strong&gt; You can find the complete source code, Dockerfiles, and Kubernetes manifests for this entire series on GitHub: &lt;a href="https://github.com/MitraKumar/kube-prac-calculator-app" rel="noopener noreferrer"&gt;MitraKumar/kube-prac-calculator-app&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1: Enable Kubernetes in Docker Desktop
&lt;/h2&gt;

&lt;p&gt;By default, Docker Desktop runs only the Docker engine. Enabling Kubernetes is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Docker Desktop&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Settings gear icon&lt;/strong&gt; in the top-right corner.&lt;/li&gt;
&lt;li&gt;In the left navigation menu, click on &lt;strong&gt;Kubernetes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Check the box labeled &lt;strong&gt;Enable Kubernetes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;(Optional)&lt;/em&gt; You can leave "Show system containers" unchecked for now to keep your &lt;code&gt;docker ps&lt;/code&gt; output clean.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Apply &amp;amp; restart&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 2: Verify &lt;code&gt;kubectl&lt;/code&gt; and Cluster Connection
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;kubectl&lt;/code&gt; is the official command-line tool for communicating with a Kubernetes cluster. Docker Desktop automatically installs and configures &lt;code&gt;kubectl&lt;/code&gt; for you.&lt;/p&gt;

&lt;p&gt;Let's open our terminal and verify our client version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client Version: v1.31.0
Kustomize Version: v5.4.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Configured Contexts
&lt;/h3&gt;

&lt;p&gt;Kubernetes uses a "context" to know which cluster and user account to communicate with. Docker Desktop creates a context named &lt;code&gt;docker-desktop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl config get-contexts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CURRENT   NAME             CLUSTER          AUTHINFO         NAMESPACE
*         docker-desktop   docker-desktop   docker-desktop   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the asterisk (&lt;code&gt;*&lt;/code&gt;) is not next to &lt;code&gt;docker-desktop&lt;/code&gt;, switch to it explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl config use-context docker-desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Inspect Your Local Node and Control Plane
&lt;/h2&gt;

&lt;p&gt;Now that &lt;code&gt;kubectl&lt;/code&gt; is pointed at your local cluster, let's verify cluster health:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl cluster-info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kubernetes control plane is running at https://kubernetes.docker.internal:6443
CoreDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check the cluster nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME             STATUS   ROLES           AGE   VERSION
docker-desktop   Ready    control-plane   1d    v1.31.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;code&gt;docker-desktop&lt;/code&gt; acts as both the control-plane and the worker node. It is in the &lt;code&gt;Ready&lt;/code&gt; status, meaning it is ready to schedule workloads!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Test Driving the Cluster
&lt;/h2&gt;

&lt;p&gt;To ensure everything is working as expected, let's deploy a quick standalone Nginx pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl run test-nginx &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the pod status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME         READY   STATUS    RESTARTS   AGE
test-nginx   1/1     Running   0          15s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's forward a local port to test traffic reaching the pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward pod/test-nginx 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open another terminal or your browser at &lt;code&gt;http://localhost:8080&lt;/code&gt; (or run &lt;code&gt;curl http://localhost:8080&lt;/code&gt;). You should see the welcome page for Nginx!&lt;/p&gt;

&lt;p&gt;Once verified, press &lt;code&gt;Ctrl+C&lt;/code&gt; to stop the port-forward and delete the test pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete pod test-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Useful Tips for Docker Desktop Kubernetes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How Local Images Work (The Magic Trick)&lt;/strong&gt;:
When running Docker Desktop Kubernetes, your local Docker engine and Kubernetes share the same image store. If you build an image locally:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-app:1.0 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can specify &lt;code&gt;image: my-app:1.0&lt;/code&gt; in your Kubernetes pod manifest with &lt;code&gt;imagePullPolicy: Never&lt;/code&gt; or &lt;code&gt;imagePullPolicy: IfNotPresent&lt;/code&gt;, and Kubernetes will use your local image without needing any push or registry setup!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resetting the Cluster&lt;/strong&gt;:
If something goes wrong while experimenting, you don't have to troubleshoot for hours. Go back to &lt;strong&gt;Docker Desktop Settings -&amp;gt; Kubernetes&lt;/strong&gt; and click &lt;strong&gt;Reset Kubernetes Cluster&lt;/strong&gt;. In seconds, you get a clean slate.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusion &amp;amp; What's Next
&lt;/h2&gt;

&lt;p&gt;Setting up Kubernetes locally shouldn't be intimidating. Docker Desktop gives you an instant, robust environment right out of the box with zero fuss.&lt;/p&gt;

&lt;p&gt;In the next post, we will build a real multi-service calculator application using Node.js &amp;amp; Express, package it into containers, and explore the core building blocks of Kubernetes: &lt;strong&gt;Namespaces&lt;/strong&gt;, &lt;strong&gt;Pods&lt;/strong&gt;, and &lt;strong&gt;ClusterIP Services&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Please leave a comment below if you found this guide helpful or if you run into any issues during your setup!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Multi-Agent Interview Coach</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sun, 19 Jul 2026 18:41:06 +0000</pubDate>
      <link>https://dev.to/mitrakumar/multi-agent-interview-coach-5glm</link>
      <guid>https://dev.to/mitrakumar/multi-agent-interview-coach-5glm</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is my submission for &lt;a href="https://dev.to/deved/build-multi-agent-systems"&gt;DEV Education Track: Build Multi-Agent Systems with ADK&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Preparing for technical interviews can be overwhelming. I wanted to build a tool that doesn't just give generic questions, but actually analyzes my specific resume to challenge my unique skill set. This led me to build a multi-agent system using the ADK. This multi-agent system, will take user resume and extracts their profile for generating Interview Questions specialized to the candidate profile. The agents communicate in a sequential loop: The Profiler extracts data -&amp;gt; The Interviewer generates questions -&amp;gt; The Judge validates them. If the Judge rejects a question, the Interviewer re-drafts it, ensuring only high-quality, resume-relevant questions make it to the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud Run Embed
&lt;/h2&gt;


&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://interview-web-app-621657849809.asia-south1.run.app/"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Your Agents
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Profiler
&lt;/h3&gt;

&lt;p&gt;Receives a resume PDF GCS path, downloads it in-memory, parses the text content, and builds a summary of skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interviewer
&lt;/h3&gt;

&lt;p&gt;Reads the candidate summary and drafts 3 technical interview questions designed to test the boundaries of their experience.&lt;/p&gt;

&lt;p&gt;Analyzes the drafted questions. Passes the iteration if they are resume-specific; rejects/fails them if they are too generic. &lt;/p&gt;

&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;p&gt;This project was a fantastic weekend challenge. Working through the Google Codelab gave me a solid grasp of agent-based architectures, specifically implementing Agent, LoopAgent, and SequentialAgent to create a robust workflow.&lt;/p&gt;

&lt;p&gt;A few key technical takeaways included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Managing Statelessness&lt;/strong&gt;: Learning to handle agent sessions in a Cloud Run environment was a great lesson in explicit session lifecycle management.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloud Integration&lt;/strong&gt;: Integrating Google Cloud Storage for file handling taught me how to bridge in-memory document processing with persistent cloud storage efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployment Architecture&lt;/strong&gt;: Mastering the transition from local development to a containerized, production-ready Cloud Run deployment provided deep insights into modern backend orchestration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Check the Code from Github: &lt;a href="https://github.com/MitraKumar/interview-coach" rel="noopener noreferrer"&gt;https://github.com/MitraKumar/interview-coach&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>buildmultiagents</category>
      <category>gemini</category>
      <category>adk</category>
    </item>
    <item>
      <title>Magic behind DDEV &amp; Lando Routing</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Tue, 28 Oct 2025 11:14:57 +0000</pubDate>
      <link>https://dev.to/mitrakumar/magic-behind-ddev-lando-routing-37m</link>
      <guid>https://dev.to/mitrakumar/magic-behind-ddev-lando-routing-37m</guid>
      <description>&lt;p&gt;Today we'll discuss how the these docker based local environments for development like ddev or lando works.&lt;br&gt;
These tools makes everything magical, and the end user just needs to setup a project run commands like &lt;code&gt;ddev start&lt;/code&gt; or &lt;code&gt;lando start&lt;/code&gt; and all the docker containers will spins up and the user will be able to visit the web page by with the url &lt;em&gt;.ddev.site&lt;/em&gt; or in case of lando &lt;em&gt;.lndo.site&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;😉 Spoiler Alert all these tools uses cheeky DNS mapping &amp;amp; reverse proxy like tool &lt;strong&gt;traefik&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This guide is perfect for beginners or intermediate developers looking to understand how containerized local dev environments like DDEV use Traefik for networking.&lt;/p&gt;

&lt;p&gt;If you're new to tools like DDEV or Lando and don't know what they are used for, they are local development environment based on Docker. You can easily setup a development environment for Drupal or Wordpress or MERN/MEAN and many other frameworks. As they use Docker containers for installing dependencies local development becomes very easy.&lt;/p&gt;

&lt;p&gt;Let's take an example with DDEV.&lt;br&gt;
In DDEV you can setup a Wordpress Project simply with the following commands, considering DDEV is already setup on your system.&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;cd&lt;/span&gt; &amp;lt;project-directory-name&amp;gt;
ddev config &lt;span class="nt"&gt;--project-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wordpress &lt;span class="nt"&gt;--docroot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
ddev wp core download
ddev wp core &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://&amp;lt;project-directory-name&amp;gt;.ddev.site &lt;span class="nt"&gt;--title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Site Name&amp;gt;"&lt;/span&gt;
&lt;span class="nt"&gt;--admin_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="nt"&gt;--admin_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="nt"&gt;--admin_email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now after setting up the project, &lt;code&gt;ddev start&lt;/code&gt; will spin up all the containers &amp;amp; run your site in the URL &lt;em&gt;https://&amp;lt;project-directory-name&amp;gt;.ddev.site&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today we'll discuss how this internal routing of https://&amp;lt;project-directory-name&amp;gt;.ddev.site works to your local site.&lt;/p&gt;

&lt;p&gt;This is mainly done in two phases&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DNS Mapping&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;DNS or Domain Name Server mapping is done to assign an IP address to a specific domain. When you type &lt;a href="https://google.com" rel="noopener noreferrer"&gt;https://google.com&lt;/a&gt; on the browser. The browser doesn't know what server to request &amp;amp; how it can serve the html. The domain &lt;a href="https://google.com" rel="noopener noreferrer"&gt;https://google.com&lt;/a&gt; is being resolved to a IP &amp;amp; then the client or the browser requests the server. This is called DNS Mapping.&lt;br&gt;
Now in order the browser to know that accessing a site with the URL https://&amp;lt;project-directory-name&amp;gt;.ddev.site needs to request it's own machine where the project belongs. The URL needs to be resolve to &lt;em&gt;127.0.0.1&lt;/em&gt;.&lt;br&gt;
This custom DNS mapping is created for DDEV &amp;amp; Lando.&lt;br&gt;
You can check this by resolving the URLs *.ddev.site or *.lndo.site&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%2Ffq9d5pukckctezwu7amc.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%2Ffq9d5pukckctezwu7amc.png" alt="DNS Map for *.ddev.site being resolved to 127.0.0.1" width="800" height="482"&gt;&lt;/a&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%2Fvtjj6yvq1ycz5gnxedk3.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%2Fvtjj6yvq1ycz5gnxedk3.png" alt="DNS Map for *.lndo.site being resolved to 127.0.0.1" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this simple way, ddev or lando creates custom URL for local Development.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Proxy Server — Enter Traefik&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that DNS mapping ensures *.ddev.site (or *.lndo.site) resolves to your local machine (127.0.0.1), the next question is: how does your browser’s request for&lt;br&gt;
https://&amp;lt;project-directory-name&amp;gt;.ddev.site actually reach the correct container running your project?&lt;/p&gt;

&lt;p&gt;This is where Traefik steps in.&lt;/p&gt;

&lt;p&gt;Traefik is a modern reverse proxy and load balancer designed for containerized environments. It automatically discovers running containers and routes incoming HTTP/HTTPS requests to the right service — all without you lifting a finger.&lt;/p&gt;

&lt;p&gt;In DDEV (and similarly in Lando), Traefik runs as a global container that listens on ports 80 (HTTP) and 443 (HTTPS). When you run ddev start, your project containers register themselves with Traefik through Docker labels. These labels contain routing information — such as the domain name (.ddev.site) and the internal container port (like 80 or 8080).&lt;/p&gt;

&lt;p&gt;Here’s what happens under the hood when you visit https://&amp;lt;project-directory-name&amp;gt;.ddev.site:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser Request: Your browser sends a request to https://&amp;lt;project-directory-name&amp;gt;.ddev.site.&lt;/li&gt;
&lt;li&gt;DNS Resolution: The *.ddev.site wildcard DNS resolves to 127.0.0.1, pointing to your local machine.&lt;/li&gt;
&lt;li&gt;Traefik Intercepts: The request hits Traefik’s HTTPS listener (port 443). Traefik inspects the hostname .ddev.site and checks its internal routing table.&lt;/li&gt;
&lt;li&gt;Container Discovery: Traefik, using Docker’s API, has already discovered that a container labeled Host(.ddev.site) exists. It knows this container’s internal IP and port.&lt;/li&gt;
&lt;li&gt;Secure Routing: Traefik forwards the request securely to the correct container. It also handles SSL/TLS termination — meaning your site works seamlessly over HTTPS using Traefik’s automatically generated certificates.&lt;/li&gt;
&lt;li&gt;Response: The container serves your app, and Traefik relays the response back to the browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So while it looks like you’re visiting a remote, hosted website at https://&amp;lt;project-directory-name&amp;gt;.ddev.site, you’re actually interacting with your own machine. Traefik quietly proxies everything behind the scenes — no hosts file edits, no manual configuration.&lt;/p&gt;

&lt;p&gt;Diving into the details of how these local development environments handled routing was a great learning for me. In a future article we can try and implement similar kind of model with simple docker &amp;amp; docker compose and run our local sites with custom URL.&lt;/p&gt;

&lt;p&gt;Please leave a comment below, if you've like this article or have any kind of queries or suggestions for me.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>docker</category>
      <category>backend</category>
    </item>
    <item>
      <title>Setting up mailcow on mail server on an EC2 instance</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sun, 13 Apr 2025 09:55:31 +0000</pubDate>
      <link>https://dev.to/mitrakumar/setting-up-mailcow-on-mail-server-on-an-ec2-instance-2bf6</link>
      <guid>https://dev.to/mitrakumar/setting-up-mailcow-on-mail-server-on-an-ec2-instance-2bf6</guid>
      <description>&lt;p&gt;Recently I've been exploring how we can create our own mail server with our own custom domains.&lt;/p&gt;

&lt;p&gt;In my research I've come across a tool named &lt;code&gt;mailcow&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So let's dive in to learn about &lt;code&gt;mailcow&lt;/code&gt;, and how we can set it up in an AWS EC2 instance.&lt;/p&gt;

&lt;h1&gt;
  
  
  So what is &lt;code&gt;mailcow&lt;/code&gt;?
&lt;/h1&gt;

&lt;p&gt;From the official documentation, it is given that....&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mailcow: dockerized is an open source groupware/email suite based on docker.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;mailcow basically generates a docker-compose file for helping setup a mail server. It already comes with preconfigured components such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;acme - For creating Let's encrypt Certificates.&lt;/li&gt;
&lt;li&gt;ClamAV - For scanning Anti-virus&lt;/li&gt;
&lt;li&gt;Devcot - For IMAP/POP servers&lt;/li&gt;
&lt;li&gt;MariaDB - For storing user information&lt;/li&gt;
&lt;li&gt;SOGo - Integrated Webmailer&lt;/li&gt;
&lt;li&gt;Memcached - Cache for the webmailer SOGo&lt;/li&gt;
&lt;li&gt;Nginx - Web server for components of the stack&lt;/li&gt;
&lt;li&gt;Olefy - Analysis of Office documents for viruses, macros, etc.&lt;/li&gt;
&lt;li&gt;PHP - Programming language of most web-based mailcow applications&lt;/li&gt;
&lt;li&gt;Postfix - MTA (Mail Transfer Agent) for e-mail traffic on the Internet&lt;/li&gt;
&lt;li&gt;Redis - Storage for spam information, DKIM key, etc.&lt;/li&gt;
&lt;li&gt;Rspamd - Spam filter with automatic learning of spam mails&lt;/li&gt;
&lt;li&gt;Unbound - Integrated DNS server for verifying DNSSEC etc.&lt;/li&gt;
&lt;li&gt;etc. etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How would we install mailcow then?
&lt;/h1&gt;

&lt;p&gt;Installing &amp;amp; setting up a mailcow server is very easy, you would just need to basically run a docker-compose file for installation. Then everything can be done from the UI.&lt;/p&gt;

&lt;p&gt;But, there are some prerequisites we need to follow....&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up an EC2 instance.
&lt;/h2&gt;

&lt;p&gt;Our mail goal here would be to create an EC2 instance with sufficient RAM &amp;amp; necessary open ports on the server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: For best experience it is recommended to use atleast 4GB RAM, that's why we are using &lt;code&gt;t3.medium&lt;/code&gt; here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the creation of the instance I'm using terraform.&lt;/p&gt;

&lt;p&gt;You can find the full source code from &lt;a href="https://github.com/MitraKumar/mailcow-server-infra" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First of all for our server we need a few necessary ports to be opened.&lt;br&gt;
We can create a security group for this requirement like this...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"mailcow-security-group"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mailcow-sg"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow TLS inbound traffic"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; 
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;465&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;465&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;143&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;143&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;993&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;993&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;995&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;995&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4190&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4190&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mailcow-sg"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&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;p&gt;Now, we can create a EC2 instance with the Ubuntu image with the above security group, like this...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"mailcow_server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;ami&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0e35ddab05955cf57"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.medium"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;key_name&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_key_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key_name&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mailcow-security-group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;user_data&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="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;module}&lt;/span&gt;&lt;span class="s2"&gt;/install-script.sh"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mailcow-vm"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&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;p&gt;During the creation of the EC2 instance we are including an install script for installing Docker &amp;amp; Docker compose.&lt;br&gt;
Here is the script....&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="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting Docker Install"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/install-logs"&lt;/span&gt;

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

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

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;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; docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Completed Docker Install"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/install-logs"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Manage Docker as a non-root user"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"/home/ubuntu/install-logs"&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, that we have setup the EC2 instance as needed,  let's move on to our next requirements....&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up an DNS for the mail server
&lt;/h2&gt;

&lt;p&gt;First of all we need a domain for this step, you can buy it from anywhere like namecheap or godady&lt;/p&gt;

&lt;p&gt;I've bought my domain from namecheap, and added my domain to cloudflare for managing various DNS records.&lt;/p&gt;

&lt;p&gt;For the basic mailserver we need atleast two DNS records - MX Record, A Record.&lt;/p&gt;

&lt;h3&gt;
  
  
  MX Record
&lt;/h3&gt;

&lt;p&gt;A MX Record is basically used to point DNS servers where your domain is located.&lt;/p&gt;

&lt;p&gt;Suppose someone trying to send a mail from &lt;a href="mailto:a@gmail.com"&gt;a@gmail.com&lt;/a&gt; to your domain &lt;a href="mailto:user@yoursite.com"&gt;user@yoursite.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, the problem is how gmail would figure out where this &lt;a href="mailto:user@yoursite.com"&gt;user@yoursite.com&lt;/a&gt; is located. So gmail would the DNS server a MX record for yoursite.com.&lt;/p&gt;

&lt;p&gt;We generally add a MX record for your domain to mail.yourdomain.com&lt;/p&gt;

&lt;p&gt;For example, if you have a domain yousite.com&lt;/p&gt;

&lt;p&gt;You would add a MX Record, like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ -&amp;gt; mail.yoursite.com 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Record
&lt;/h3&gt;

&lt;p&gt;An A Record, is used to tell the DNS Servers where your site is.&lt;/p&gt;

&lt;p&gt;Following the given example above, after adding the MX Record, you would need to add the A Record like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mail.yourdomain.com -&amp;gt; IP Address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we are done with the server configs, let dive in with the actual setup...&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Mailcow
&lt;/h2&gt;

&lt;p&gt;Now that we have setup our server properly, for installing mailcow we need to just basically download the git repo of the mailcow and run the docker image...&lt;/p&gt;

&lt;p&gt;Let's see, how can we do that...&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify current user to have proper privilage
&lt;/h3&gt;

&lt;p&gt;Before cloning the git repo, first we have to check if we have proper user privileges&lt;/p&gt;

&lt;p&gt;Run 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;&lt;span class="nb"&gt;umask&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of the above command should be &lt;code&gt;0022&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For EC2 instance, by default the output should not be as expected. We need to switch the user to root, for this to work.&lt;br&gt;
You can do that by running&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clone mailcow repo
&lt;/h3&gt;

&lt;p&gt;Let's clone to the repo, into &lt;code&gt;/opt&lt;/code&gt; directory and generate the configurations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd /opt
$ git clone git clone https://github.com/mailcow/mailcow-dockerized
$ cd mailcow-dockerized
$ ./generate_config.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: If you wan to change any of the configuration, you can edit the file &lt;code&gt;mailcow.conf&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Run the image
&lt;/h3&gt;

&lt;p&gt;Now that we have setup all the things, let's run the docker compose in the background, and hope everything is working properly.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;After the running the image, visit the domain  you have setup with the A record. Like in our example we will open &lt;code&gt;yourdomain.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now that you have installed mailcow, you manage various admin tasks from the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Few links for managing mailcow
&lt;/h3&gt;

&lt;p&gt;Admin portal can be accessed via &lt;code&gt;/admin&lt;/code&gt;. The default username/password is &lt;code&gt;admin/moohoo&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;All Department portal can be accessed via &lt;code&gt;/department&lt;/code&gt;. The default username/password is &lt;code&gt;department/moohoo&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using markdown to manage content to NextJS App Router</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Tue, 26 Mar 2024 02:12:43 +0000</pubDate>
      <link>https://dev.to/mitrakumar/using-markdown-to-manage-content-to-nextjs-app-router-420j</link>
      <guid>https://dev.to/mitrakumar/using-markdown-to-manage-content-to-nextjs-app-router-420j</guid>
      <description>&lt;p&gt;Recently I have been building my portfolio site using NextJs/TailwindCSS.&lt;br&gt;
Here is the link to the site - &lt;a href="https://portfolio-beta-ten-13.vercel.app/" rel="noopener noreferrer"&gt;Visit here&lt;/a&gt;&lt;br&gt;
It's still a work in progress, here is the &lt;a href="https://github.com/MitraKumar/portfolio?tab=readme-ov-file" rel="noopener noreferrer"&gt;source code&lt;/a&gt; for the site.&lt;/p&gt;

&lt;p&gt;In the site I have used markdown files to manage the Projects data on the site.&lt;/p&gt;

&lt;p&gt;Today we will discuss how we can use the markdown files to generate contents for a NextJS site with Server Components and integrate with Typescript.&lt;/p&gt;

&lt;p&gt;So let's dive in...&lt;/p&gt;


&lt;h2&gt;
  
  
  Why &lt;code&gt;next-mdx-remote&lt;/code&gt;...
&lt;/h2&gt;

&lt;p&gt;If we follow the official &lt;a href="https://nextjs.org/docs/pages/building-your-application/configuring/mdx" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; there it is mentioned that how we can basically use &lt;code&gt;.mdx&lt;/code&gt; to generate our JSX components which we can render on our site.&lt;/p&gt;

&lt;p&gt;But following this way can be a bit difficult as it will be very difficult to manage dynamically generated content or if the markdown is coming from somewhere else like some CMS or some other place from the file system.&lt;/p&gt;

&lt;p&gt;To solve this issue we can use a community package &lt;code&gt;next-mdx-remote&lt;/code&gt;. This package actually compiles a given markdown string and generates HTML, so it will solve our problem of maintaining markdown if we want to scale in future and it is coming from some other database or something.&lt;/p&gt;
&lt;h2&gt;
  
  
  Integrating &lt;code&gt;next-mdx-remote&lt;/code&gt; to React Server components in NextJS.
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;next-mdx-remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Basic Usage
&lt;/h3&gt;

&lt;p&gt;As we are using react server components we can simply fetch the markdown data from a file and display it with the &lt;code&gt;MDXRemote&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// page.tsx&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;IndexPage&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;markdown_data&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;getProject&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="nx"&gt;markdown_data&lt;/span&gt; &lt;span class="o"&gt;===&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&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;&amp;lt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MDXRemote&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;markdown_data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Advance Usage
&lt;/h3&gt;

&lt;p&gt;We can setup our own custom components when generating the HTML form the Markdown files.&lt;/p&gt;

&lt;p&gt;For this we can create our own component which will wrap the libraries &lt;code&gt;MDXRemote&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// mdx-remote.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;MDXRemote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MDXRemoteProps&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;next-mdx-remote/rsc&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;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h1&lt;/span&gt;&lt;span class="dl"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HeaderOne&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/HeaderOne&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="na"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h2&lt;/span&gt;&lt;span class="dl"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HeaderTwo&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/HeaderTwo&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="dl"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Paragragraph&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Paragragraph&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="na"&gt;img&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MdxImage&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MdxLink&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/MdxLink&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CustomMDX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MDXRemoteProps&lt;/span&gt;&lt;span class="p"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MDXRemote&lt;/span&gt;
      &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&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;components&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And these HeaderOne, HeaderTwo, etc can be simple components with our custom styles.&lt;/p&gt;

&lt;p&gt;For example...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// HeaderOne.tsx&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;ComponentProps&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HeaderOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h1&lt;/span&gt;&lt;span class="dl"&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="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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mb-6 mt-16 text-5xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MDXImage.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&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;next/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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MdxImageProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MdxImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MdxImageProps&lt;/span&gt;&lt;span class="p"&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"border-4 border-primary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now we have created our custom wrapper component for the &lt;code&gt;MDXRemote&lt;/code&gt; we can use this in our server component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /projects/[title]/page.tsx&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;IndexPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;params&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="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markdown_data&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;getProjectWithTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&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="nx"&gt;markdown_data&lt;/span&gt; &lt;span class="o"&gt;===&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&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;&amp;lt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MDXRemote&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;markdown_data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now as we are using NextJS we can further more optise our site by staticly generating the page with &lt;code&gt;generateStaticParams&lt;/code&gt; function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /projects/[title]/page.tsx&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateStaticParams&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getProjectTitles&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;p&gt;The source code for the above can be found &lt;a href="https://github.com/MitraKumar/portfolio" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;So, in conclusion we discussed how we can integrate this community package &lt;code&gt;next-mdx-remote&lt;/code&gt; for managing content with markdown and also how we can use Typescript to add proper types and have proper type safe development.&lt;/p&gt;

&lt;p&gt;If you have any discussions regarding the topic feel free to comment below.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>typescript</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Use bunx for running npm packages with lightning fast speed.</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Mon, 11 Sep 2023 20:34:38 +0000</pubDate>
      <link>https://dev.to/mitrakumar/use-bunx-for-running-npm-packages-with-lightning-fast-speed-2p32</link>
      <guid>https://dev.to/mitrakumar/use-bunx-for-running-npm-packages-with-lightning-fast-speed-2p32</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Use Case for the package&lt;/li&gt;
&lt;li&gt;Comparing with npx&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bun&lt;/code&gt; is the latest and greatest runtime for Javascript  which ships with many features like built in Typescript support and testing support and etc. etc.. bunx, alias for &lt;code&gt;bun x&lt;/code&gt;, is a package manger for bun which can be used for auto installing and running npm packages. We can you &lt;code&gt;bunx&lt;/code&gt; just similarly like we use &lt;code&gt;npx&lt;/code&gt; or &lt;code&gt;yarn dlx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://twitter.com/jarredsumner/status/1606163655527059458" rel="noopener noreferrer"&gt;this&lt;/a&gt; twitter post, bunx is 100x faster than npx, which means we can use bunx and run our npm cli with lightning fast speed.&lt;/p&gt;

&lt;p&gt;Today we will discuss an use case of running a npm package with bunx and compare the result with running it with npx.&lt;/p&gt;

&lt;p&gt;You can view the package from &lt;a href="https://www.npmjs.com/package/phil-s-osophy" rel="noopener noreferrer"&gt;here&lt;/a&gt;, it is a simple package that I wrote which generates a random quote of Phil Dunphy from Mordern Family. You can also view a &lt;a href="https://dev.to/mitrakumar/journey-of-creating-and-publishing-a-nodejs-package-with-typescript-eslint-support-3gh7"&gt;post&lt;/a&gt; that I have written with my learnings and take aways from creating this package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case for the package
&lt;/h2&gt;

&lt;p&gt;As the package &lt;code&gt;phil-s-osophy&lt;/code&gt; just spits out basic string output we can pipe that to any program, like I have used to pipe the value in &lt;code&gt;cowsay&lt;/code&gt; and added the whole thing at the bottom of my zshrc file to show text at every shell creation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx phil-s-osophy | bunx cowsay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Comparing with npx
&lt;/h2&gt;

&lt;p&gt;To compare my changes with the npx, I have created a function which will run the shell few times and time it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;timezsh&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 10&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; /usr/bin/time &lt;span class="nv"&gt;$SHELL&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So after running both my program with bunx and npx I timed it to aroud &lt;code&gt;0.3 to 0.4s&lt;/code&gt; when running with bunx but when running with npx the program took around &lt;code&gt;2s&lt;/code&gt; to ran.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: there are many more things happening in my zshrc file, but I am ignoring this as they are running in bot scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Left running - &lt;code&gt;bunx phil-s-osophy | bunx cowsay&lt;/code&gt;&lt;br&gt;
Right running - &lt;code&gt;npx phil-s-osophy | npx cowsay&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%2Fsg6ixcxkihkrdy0rjjvl.jpeg" 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%2Fsg6ixcxkihkrdy0rjjvl.jpeg" alt="comapare bunx v npx - screengrab" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;So, in conclusion we have seen how bunx is so much faster than npx and how we can use this lightning fast speed to install and use packages for that great developer experience.&lt;/p&gt;

&lt;p&gt;For more information about the bun, you can visit &lt;a href="https://bun.sh/docs" rel="noopener noreferrer"&gt;bun.sh&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>npm</category>
      <category>bunjs</category>
    </item>
    <item>
      <title>Journey of creating and publishing a nodejs package with typescript &amp; eslint support</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Fri, 08 Sep 2023 22:26:38 +0000</pubDate>
      <link>https://dev.to/mitrakumar/journey-of-creating-and-publishing-a-nodejs-package-with-typescript-eslint-support-3gh7</link>
      <guid>https://dev.to/mitrakumar/journey-of-creating-and-publishing-a-nodejs-package-with-typescript-eslint-support-3gh7</guid>
      <description>&lt;p&gt;Today we will discuss how I've written a npm package and published it to &lt;a href="https://www.npmjs.com/package/phil-s-osophy" rel="noopener noreferrer"&gt;npmjs&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can checkout the codebase from &lt;a href="https://github.com/MitraKumar/phil-s-osophy" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a simple package which just generates random quotes from Phil Dunphy, the lovable and hilarious character from the TV show Modern Family.  I have chosen Phil Dunphy as I love Mordern Family and he is one of my favorite caracter from the show.&lt;/p&gt;

&lt;p&gt;The target of this package was not to create something unique and useful for everyone (though if you are a fan of Phill Dunphy you may love this idea 😉), but it was to learn how we can use these technologies to build packages in a simple and efficient manner.&lt;/p&gt;

&lt;h4&gt;
  
  
  Technology Used
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Typescript for writing the business logic and have used &lt;a href="https://tsup.egoist.dev/" rel="noopener noreferrer"&gt;tsup&lt;/a&gt; for compiling to javascript files. &lt;/li&gt;
&lt;li&gt;ESlint for linting the typescript code&lt;/li&gt;
&lt;li&gt;Also have used Github Actions for linting the code whenever a new pull request is being created.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Setting up local environment for creating packages
&lt;/h4&gt;

&lt;p&gt;For setting up a local environment, we can start like a basic node js project.&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;my_project
&lt;span class="nb"&gt;cd &lt;/span&gt;my_prject
npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that we need to add the main entry point of our package as well as a bin file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"phil-s-osophy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Phil Dunphy's amazing life lessons from Mordern Family."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.mjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"phil-o-osophy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git+https://github.com/MitraKumar/phil-s-osophy.git"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MitraKumar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bugs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/MitraKumar/phil-s-osophy/issues"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"homepage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/MitraKumar/phil-s-osophy#readme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my scenario since it I am compiling the javascript with tsup I've added the files in here.&lt;/p&gt;

&lt;p&gt;And in the typescript file we need to add shebang in front of the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#! /usr/bin/env node
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After updating all this we can simply run &lt;code&gt;npm install -g .&lt;/code&gt; from the project directory to use the package.&lt;/p&gt;

&lt;h4&gt;
  
  
  Project Setup with Typescript
&lt;/h4&gt;

&lt;p&gt;The main business logic for the package is written in typescript and the logic is very simple, it's just accessing a random element from a list of quotes and printing it out. Again the project was not to create anything unique, it is just fun enough for me to learn these things.&lt;/p&gt;

&lt;p&gt;For compiling typescript file I have used tsup as a dev dependency, which is very useful and intuitive for compiling to javascript files. To make this process seamless I've added a npm script to build the project.&lt;/p&gt;

&lt;p&gt;For the configuration of tsup we need to create a &lt;code&gt;tsup.config.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&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="s1"&gt;tsup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es2020&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cjs&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="s1"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;splitting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sourcemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then after adding this I added the build command in npm scripts and voila it all worked seamlessly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"phil-s-osophy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsup ./src"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is really helpful as we get the power of types and the seamless process of compiling to javascript files.&lt;/p&gt;

&lt;h4&gt;
  
  
  Linting with ESLint
&lt;/h4&gt;

&lt;p&gt;Linting is must in a project, this is a small simple enough project but for a live project with many contributors linting is must otherwise without following a proper coding standard it will be difficult over time to maintain a project.&lt;/p&gt;

&lt;p&gt;I have used ESlint for checking the linting of the project. Setting up ESLint is simple enough but we have to add few dependencies to help us out here...&lt;/p&gt;

&lt;p&gt;in the package.json we need to add few of the dev dependencies such as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@typescript-eslint/eslint-plugin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@typescript-eslint/parser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8.48.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can install this dependencies by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that we need to add a &lt;code&gt;.eslintrc&lt;/code&gt; file, with some basic rulesets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@typescript-eslint/parser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"@typescript-eslint"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"eslint:recommended"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"plugin:@typescript-eslint/eslint-recommended"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"plugin:@typescript-eslint/recommended"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@typescript-eslint/no-explicit-any"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"indent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"linebreak-style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unix"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"quotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"double"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"semi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"always"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And all the configurations are done, now we can simply add a npm script &lt;code&gt;eslint ./src --ext .ts&lt;/code&gt; to lint our files.&lt;/p&gt;

&lt;p&gt;But this will cause an issue as ESLint will not trigger any typescript error, for that we can simply check it with &lt;code&gt;tsc --noEmit ./src/index.ts&lt;/code&gt;. So for linting we can simply add a script like this &lt;code&gt;tsc --noEmit ./src/index.ts; eslint ./src --ext .ts&lt;/code&gt; to run both of them sequentially.&lt;/p&gt;

&lt;p&gt;Also we can add a script to fix the linting errors with ESlint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  ...
  "scripts": {
    "lint": "tsc --noEmit ./src/index.ts; eslint ./src --ext .ts",
    "fix": "eslint ./src --ext .ts --fix",
    ...
  },
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila, linting is also setup in our project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up Github Actions for checking coding standards
&lt;/h4&gt;

&lt;p&gt;In a real world we probably cannot expect everyone to properly lint code and the Pushing it. So we need to setup a Github Action to generate a check for linting errors if any whenever a PR is being generated.&lt;/p&gt;

&lt;p&gt;Setting up such action is pretty easy, we just need create a yml file in the directory &lt;code&gt;.github/workflows&lt;/code&gt; like I've created a file &lt;code&gt;.github/workflows/linter.yml&lt;/code&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="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Code Checker&lt;/span&gt;

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

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;linting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checking coding standards &amp;amp; type checking.&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup node environment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm clean-install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run linting &amp;amp; type checking&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run lint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow file is mainly divided into three sections&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name - Name of the workflow&lt;/li&gt;
&lt;li&gt;Event - On which event the workflow will trigger, like in this scenario it will trigger on creating a Pull request against the branch &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Jobs - What jobs will run in this workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jobs also have several aspects to understand, the main thing being &lt;code&gt;steps&lt;/code&gt;&lt;br&gt;
Steps basically mentions what the a job will do in a sequential order, like here it is mentioned that the job will first checkout the repo, then setup node environment then install all dependencies and then run our lint script from above.&lt;/p&gt;

&lt;p&gt;And that's it, now creating a PR will automatically run this jobs and will show a green check mark ✅&lt;br&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%2Fvm6hp0k9syylsgq4t548.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%2Fvm6hp0k9syylsgq4t548.png" alt="Github Screenshot for Workflow Action Checks" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;So in conclusion I will say, this was a fun simple enough project which helped me learn a lot of things. And if any of you have any idea of improving the project, even if it's a change in README file, you are more than welcome to submit a Pull Request and I will be happy to merge it.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>beginners</category>
      <category>devops</category>
      <category>node</category>
    </item>
    <item>
      <title>mordern_navbar</title>
      <dc:creator>Kaushik Mitra</dc:creator>
      <pubDate>Sun, 05 Jan 2020 00:01:24 +0000</pubDate>
      <link>https://dev.to/mitrakumar/mordernnavbar-2oj5</link>
      <guid>https://dev.to/mitrakumar/mordernnavbar-2oj5</guid>
      <description>&lt;p&gt;my first post in this community, hope to get some love.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/mitrakumar/embed/mdyqNVW?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>codepen</category>
    </item>
  </channel>
</rss>
