<?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: Azam Akram</title>
    <description>The latest articles on DEV Community by Azam Akram (@azam-akram).</description>
    <link>https://dev.to/azam-akram</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3974919%2Fe4b398b8-3571-4282-b0f4-2d73a6e38962.jpg</url>
      <title>DEV Community: Azam Akram</title>
      <link>https://dev.to/azam-akram</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/azam-akram"/>
    <language>en</language>
    <item>
      <title>I Built a Collection of 100+ Free Developer Tools That Run Entirely in the Browser</title>
      <dc:creator>Azam Akram</dc:creator>
      <pubDate>Tue, 09 Jun 2026 00:01:16 +0000</pubDate>
      <link>https://dev.to/azam-akram/i-built-a-collection-of-100-free-developer-tools-that-run-entirely-in-the-browser-4421</link>
      <guid>https://dev.to/azam-akram/i-built-a-collection-of-100-free-developer-tools-that-run-entirely-in-the-browser-4421</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Collection of 100+ Free Developer Tools That Run Entirely in the Browser
&lt;/h1&gt;

&lt;p&gt;As developers, we constantly run into small tasks that interrupt our workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Formatting JSON&lt;/li&gt;
&lt;li&gt;Decoding JWTs&lt;/li&gt;
&lt;li&gt;Converting timestamps&lt;/li&gt;
&lt;li&gt;Comparing configuration files&lt;/li&gt;
&lt;li&gt;Generating UUIDs&lt;/li&gt;
&lt;li&gt;Building cron expressions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these tasks don't require a full application or a paid SaaS product. They just need a simple utility that works immediately.&lt;/p&gt;

&lt;p&gt;That's why I started building &lt;strong&gt;Solution Toolkit&lt;/strong&gt; — a collection of browser-based developer tools focused on everyday development tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Goals
&lt;/h2&gt;

&lt;p&gt;When building the toolkit, I wanted it to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free to use&lt;/li&gt;
&lt;li&gt;No sign-up required&lt;/li&gt;
&lt;li&gt;Privacy-first&lt;/li&gt;
&lt;li&gt;Fast and lightweight&lt;/li&gt;
&lt;li&gt;Available directly in the browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most tools run entirely client-side, so data never leaves your device.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Included?
&lt;/h2&gt;

&lt;p&gt;Some of the categories currently available:&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON &amp;amp; API Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JSON Formatter&lt;/li&gt;
&lt;li&gt;JSON Validator&lt;/li&gt;
&lt;li&gt;JSON Escape / Unescape&lt;/li&gt;
&lt;li&gt;JSON Converters&lt;/li&gt;
&lt;li&gt;JWT Debugger&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Encoding &amp;amp; Decoding
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Base64 Encoder / Decoder&lt;/li&gt;
&lt;li&gt;URL Encoder / Decoder&lt;/li&gt;
&lt;li&gt;HTML Encoder / Decoder&lt;/li&gt;
&lt;li&gt;ASCII Converter&lt;/li&gt;
&lt;li&gt;GZip Base64 Decoder&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Formatting &amp;amp; Validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SQL Formatter&lt;/li&gt;
&lt;li&gt;YAML Formatter&lt;/li&gt;
&lt;li&gt;CSS Formatter&lt;/li&gt;
&lt;li&gt;JavaScript Formatter&lt;/li&gt;
&lt;li&gt;HTML Formatter&lt;/li&gt;
&lt;li&gt;Markdown Formatter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Developer Utilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;UUID Generator&lt;/li&gt;
&lt;li&gt;Cron Expression Builder&lt;/li&gt;
&lt;li&gt;Epoch Timestamp Converter&lt;/li&gt;
&lt;li&gt;QR Code Generator&lt;/li&gt;
&lt;li&gt;Hash Generators&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Image Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Image Compression&lt;/li&gt;
&lt;li&gt;Image Resizing&lt;/li&gt;
&lt;li&gt;PNG/JPG/WebP Conversion&lt;/li&gt;
&lt;li&gt;SVG Utilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Learned Building It
&lt;/h2&gt;

&lt;p&gt;A few things surprised me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Developers care a lot about privacy for utility tools.&lt;/li&gt;
&lt;li&gt;Browser APIs are powerful enough to handle many tasks entirely client-side.&lt;/li&gt;
&lt;li&gt;Small utilities often provide more daily value than large applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Looking for Feedback
&lt;/h2&gt;

&lt;p&gt;I'm actively adding new tools and improving existing ones.&lt;/p&gt;

&lt;p&gt;What browser-based developer tool do you wish existed?&lt;/p&gt;

&lt;p&gt;Or what's a utility you find yourself searching for repeatedly?&lt;/p&gt;

&lt;p&gt;I'd love to hear suggestions.&lt;/p&gt;

&lt;p&gt;🔗 Project: &lt;a href="https://www.solutiontoolkit.com" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AWS ALB Scaling: Set Up Application Load Balancer with Auto Scaling Group (ASG)</title>
      <dc:creator>Azam Akram</dc:creator>
      <pubDate>Mon, 08 Jun 2026 23:38:40 +0000</pubDate>
      <link>https://dev.to/azam-akram/aws-alb-scaling-set-up-application-load-balancer-with-auto-scaling-group-asg-2hdc</link>
      <guid>https://dev.to/azam-akram/aws-alb-scaling-set-up-application-load-balancer-with-auto-scaling-group-asg-2hdc</guid>
      <description>&lt;p&gt;In any well-architected cloud setup, managing traffic efficiently and scaling resources on demand are key to keeping your applications fast, reliable, and cost-efficient.&lt;br&gt;&lt;br&gt;
AWS makes this easy with two core services: the &lt;a href="https://aws.amazon.com/elasticloadbalancing/" rel="noopener noreferrer"&gt;&lt;strong&gt;Elastic Load Balancer (ELB)&lt;/strong&gt;&lt;/a&gt; for routing traffic, and &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/auto-scaling-groups.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Auto Scaling Groups (ASG)&lt;/strong&gt;&lt;/a&gt; for automatically adjusting compute capacity as traffic changes.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://aws.amazon.com/elasticloadbalancing/" rel="noopener noreferrer"&gt;&lt;strong&gt;AWS Elastic Load Balancer&lt;/strong&gt;&lt;/a&gt; automatically distributes incoming requests across multiple targets, such as, EC2 instances, containers, or IPs, across different &lt;strong&gt;Availability Zones (AZs)&lt;/strong&gt;. AWS offers several types of load balancers, including &lt;strong&gt;Application&lt;/strong&gt;, &lt;strong&gt;Network&lt;/strong&gt;, and &lt;strong&gt;Gateway Load Balancers&lt;/strong&gt;, each suited for different scenarios.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll set up an &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; connected to an &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; using the &lt;strong&gt;AWS CLI&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Our setup will dynamically launch and manage &lt;a href="https://docs.aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;&lt;strong&gt;EC2 instances&lt;/strong&gt;&lt;/a&gt;, organized into a &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Target Group&lt;/strong&gt;&lt;/a&gt;, ensuring smooth load distribution and high availability.&lt;/p&gt;
&lt;h2&gt;
  
  
  What We'll Do
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;strong&gt;security group&lt;/strong&gt; to allow necessary inbound traffic.
&lt;/li&gt;
&lt;li&gt;Define a &lt;strong&gt;launch template&lt;/strong&gt; with EC2 instance configuration and a &lt;strong&gt;user-data script&lt;/strong&gt; to deploy a simple web server.
&lt;/li&gt;
&lt;li&gt;Set up an &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; to handle scaling based on demand.
&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;Target Group&lt;/strong&gt; to manage and distribute traffic.
&lt;/li&gt;
&lt;li&gt;Configure an &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; and &lt;strong&gt;listener&lt;/strong&gt;, linking it to the Target Group for seamless load balancing.
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;AWS account&lt;/strong&gt;. If you don’t have one, &lt;a href="https://dev.to/blog/how-to-get-started-with-aws-in-10-minutes/"&gt;follow this quick guide to create a free-tier AWS account&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS access keys&lt;/strong&gt; configured in your local CLI environment. These are needed for deploying resources.
Check out &lt;a href="https://dev.to/blog/how-to-get-started-with-aws-in-10-minutes/#9-get-aws-keys"&gt;Section 9&lt;/a&gt; of our AWS starter guide for details.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why Combine ALB with ASG?
&lt;/h2&gt;

&lt;p&gt;Pairing an &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; with an &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; gives you a scalable, cost-effective, and fault-tolerant setup. Here’s why it’s a great combo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Scaling&lt;/strong&gt; – ASG adjusts the number of EC2 instances in real time as traffic changes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Optimization&lt;/strong&gt; – You only pay for what you use, saving costs during low-traffic hours.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Availability&lt;/strong&gt; – ALB spreads requests across healthy instances in multiple AZs, avoiding single points of failure.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Fault Tolerance&lt;/strong&gt; – If one instance fails, ALB automatically routes traffic to healthy ones, ensuring smooth performance.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Let’s dive in and start building!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Create a Security Group
&lt;/h2&gt;

&lt;p&gt;Before launching EC2 instances or attaching them to a Load Balancer, we need to define a &lt;strong&gt;security group&lt;/strong&gt;, which is a virtual firewall that controls inbound and outbound traffic. In this step, we’ll create a security group that allows &lt;strong&gt;SSH&lt;/strong&gt; (for remote access) and &lt;strong&gt;HTTP&lt;/strong&gt; (for web traffic) connections.&lt;/p&gt;

&lt;p&gt;Run the following command to create a new security group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 create-security-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; my-test-security-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Security Group for My ALB"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;your-region-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a security group in your specified AWS region. Next, we’ll add inbound rules that define what types of traffic are allowed to reach our EC2 instances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; my-test-security-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 22 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cidr&lt;/span&gt; 0.0.0.0/0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;your-region-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rule enables SSH access on port 22, allowing you to connect to the instance from your terminal or SSH client.&lt;br&gt;
We use tcp as the protocol since SSH operates over the TCP layer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Allowing SSH from all IP addresses (0.0.0.0/0) is convenient for testing, but not secure for production. In real deployments, you should restrict this to your own IP or a trusted network range, for example: &lt;code&gt;--cidr 203.0.113.25/32&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we create another inbound rule to allow HTTP traffic on port 80 over TCP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; my-test-security-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 80 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cidr&lt;/span&gt; 0.0.0.0/0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;your-region-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/ec2-security-group-console.webp"&lt;br&gt;
  alt="ec2-security-group-console"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;Let's verify the security group in the AWS Management Console:&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/ec2-security-group-gui.webp"&lt;br&gt;
  alt="ec2-security-group-gui"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Create a Launch Template
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;AWS Launch Template&lt;/strong&gt; is a reusable blueprint that defines the configuration settings for your EC2 instances, such as the AMI, instance type, key pair, security groups, and optional user-data scripts.&lt;br&gt;&lt;br&gt;
Using a launch template helps &lt;strong&gt;standardize instance launches&lt;/strong&gt; and simplifies scaling operations within an &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this step, we’ll manually create a launch template using the &lt;strong&gt;AWS Management Console&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;strong&gt;EC2 Dashboard&lt;/strong&gt; in the AWS Management Console.
&lt;/li&gt;
&lt;li&gt;In the left sidebar, choose &lt;strong&gt;Launch Templates&lt;/strong&gt;, then click &lt;strong&gt;Create launch template&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Enter a name, such as &lt;code&gt;MyLaunchTemplate&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Application and OS Images (Amazon Machine Image)&lt;/strong&gt;, select &lt;strong&gt;Amazon Linux 2023 AMI&lt;/strong&gt; (eligible for the free tier).
&lt;/li&gt;
&lt;li&gt;Choose an &lt;strong&gt;Instance type&lt;/strong&gt;, e.g., &lt;code&gt;t2.micro&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Skip the &lt;strong&gt;Key pair&lt;/strong&gt; step for now (this is fine for testing, but in production, you should always create one for SSH access).
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Network settings&lt;/strong&gt;, select the previously created &lt;strong&gt;security group (&lt;code&gt;my-test-security-group&lt;/code&gt;)&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Expand &lt;strong&gt;Advanced details&lt;/strong&gt; and paste the following &lt;strong&gt;user data&lt;/strong&gt; script to automatically install and configure a simple web server.
Finally, click &lt;strong&gt;Create launch template&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; httpd
systemctl start httpd
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;httpd

&lt;span class="c"&gt;# Get the EC2 instance's availability zone&lt;/span&gt;
&lt;span class="nv"&gt;EC2AZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"http://169.254.169.254/latest/api/token"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token-ttl-seconds: 21600"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; http://169.254.169.254/latest/meta-data/placement/availability-zone&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Create a simple HTML file with the availability zone info&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;center&amp;gt;&amp;lt;h1&amp;gt;Hello from Web Server in Availability Zone: AZID &amp;lt;/h1&amp;gt;&amp;lt;/center&amp;gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /var/www/html/index.txt
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/AZID/&lt;/span&gt;&lt;span class="nv"&gt;$EC2AZ&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; /var/www/html/index.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /var/www/html/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This user-data script runs automatically when the instance starts. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installs and enables the &lt;strong&gt;Apache (httpd)&lt;/strong&gt; web server.&lt;/li&gt;
&lt;li&gt;Fetches the &lt;strong&gt;Availability Zone&lt;/strong&gt; from the instance’s metadata.&lt;/li&gt;
&lt;li&gt;Displays a custom message in the browser showing the zone where the instance is running — useful when testing load balancing across multiple zones.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Every EC2 instance has access to &lt;strong&gt;instance metadata&lt;/strong&gt;, which contains information like instance ID, region, IP addresses, and Availability Zone.&lt;br&gt;&lt;br&gt;
AWS exposes this data through a special internal endpoint — &lt;strong&gt;&lt;code&gt;169.254.169.254&lt;/code&gt;&lt;/strong&gt; — accessible only from within the instance.&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html" rel="noopener noreferrer"&gt;Learn more about instance metadata in the official AWS documentation.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/launch-template.webp"&lt;br&gt;
  alt="launch-template"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Create an Auto Scaling Group (ASG)
&lt;/h2&gt;

&lt;p&gt;Now we will create an Auto Scaling group named "my-auto-scaling-group" using a launch template we previously created "MyLaunchTemplate." It sets the desired capacity to 2 instances, with a minimum of 1 and a maximum of 5 instances, distributed across two availability zones. The instances are launched in two different subnets within a Virtual Private Cloud (VPC) to ensure scalability and high availability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws autoscaling create-auto-scaling-group &lt;span class="nt"&gt;--auto-scaling-group-name&lt;/span&gt; my-auto-scaling-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--launch-template&lt;/span&gt; &lt;span class="s2"&gt;"LaunchTemplateName=MyLaunchTemplate"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--min-size&lt;/span&gt; 1 &lt;span class="nt"&gt;--max-size&lt;/span&gt; 5 &lt;span class="nt"&gt;--desired-capacity&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--availability-zones&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;availability-zone-1&amp;gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;availability-zone-2&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-zone-identifier&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;subnet-id-1&amp;gt;,&amp;lt;subnet-id-2&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Replace &lt;code&gt;&amp;lt;subnet-id-x&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;availablity-zone-x&amp;gt;&lt;/code&gt; according to your setup.&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/security-group.webp"&lt;br&gt;
  alt="security-group"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4: Create Target Group and Load Balancer
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a &lt;strong&gt;Target Group&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Target Group&lt;/strong&gt; defines the set of resources (such as EC2 instances) that an &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; routes incoming requests to. It continuously performs &lt;strong&gt;health checks&lt;/strong&gt; to ensure traffic is sent only to healthy instances, improving reliability and availability.&lt;/p&gt;

&lt;p&gt;In this step, we’ll create an HTTP-based target group named &lt;code&gt;my-target-group&lt;/code&gt; that listens on &lt;strong&gt;port 80&lt;/strong&gt; within your VPC. This target group will later be linked to your &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; so that any new instances launched by the ASG are automatically registered as targets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws elbv2 create-target-group &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--name&lt;/span&gt; my-target-group &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--protocol&lt;/span&gt; HTTP &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--port&lt;/span&gt; 80 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--vpc-id&lt;/span&gt; &amp;lt;vpc-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/target-group.webp"&lt;br&gt;
  alt="target-group"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Target group is created but no EC2 instances are registered yet. Instances will be registered automatically once the Auto Scaling Group is attached in a later step.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create an &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; intelligently distributes incoming HTTP and HTTPS traffic across multiple targets, such as EC2 instances, in one or more &lt;strong&gt;Availability Zones (AZs)&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The following command creates an ALB named &lt;code&gt;my-alb&lt;/code&gt;, specifying the subnets and security groups that determine where and how the load balancer operates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws elbv2 create-load-balancer &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--name&lt;/span&gt; my-alb &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--subnets&lt;/span&gt; &amp;lt;subnet-id-1&amp;gt; &amp;lt;subnet-id-2&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--security-groups&lt;/span&gt; &amp;lt;security-group-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates an &lt;strong&gt;Application Load Balancer&lt;/strong&gt; called &lt;code&gt;my-alb&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Associates it with &lt;strong&gt;two subnets&lt;/strong&gt; (usually in different Availability Zones) for high availability.
&lt;/li&gt;
&lt;li&gt;Applies the specified &lt;strong&gt;security group&lt;/strong&gt;, which defines what inbound and outbound traffic is allowed for the load balancer.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Replace &lt;code&gt;&amp;lt;subnet-id-1&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;subnet-id-2&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;security-group-id&amp;gt;&lt;/code&gt; with actual values from your AWS setup. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/create-load-balancer.webp"&lt;br&gt;
  alt="create-load-balancer"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You need to note down LoadBalancerArn, which we will use in coming sections.&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/load-balancer-setting.webp"&lt;br&gt;
  alt="load-balancer-setting"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a &lt;strong&gt;Listener&lt;/strong&gt; and Link it to the Target Group
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Listener&lt;/strong&gt; is a key component of an &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt;. It checks for incoming connection requests on a specific port and protocol, then forwards the traffic to a designated &lt;strong&gt;Target Group&lt;/strong&gt; based on defined rules.&lt;br&gt;
In this step, we’ll create an &lt;strong&gt;HTTP listener&lt;/strong&gt; on &lt;strong&gt;port 80&lt;/strong&gt; for our ALB, which will route all incoming web requests to the previously created target group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws elbv2 create-listener &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--load-balancer-arn&lt;/span&gt; &amp;lt;alb-arn&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; HTTP &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 80 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--default-actions&lt;/span&gt; &lt;span class="nv"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;forward,TargetGroupArn&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;target-group-arn&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/create-listener.webp"&lt;br&gt;
  alt="create-listener"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Attach the &lt;strong&gt;Target Group&lt;/strong&gt; to the &lt;strong&gt;Auto Scaling Group&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Target Group&lt;/strong&gt; must be linked to your &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; so that any instances launched by the ASG are automatically registered with the &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
This ensures that traffic is distributed only to healthy, actively running instances managed by the ASG.&lt;/p&gt;

&lt;p&gt;Use the following command to attach the target group to your Auto Scaling group named &lt;code&gt;ASG1&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;aws autoscaling attach-load-balancer-target-groups &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--auto-scaling-group-name&lt;/span&gt; ASG1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-group-arns&lt;/span&gt; &amp;lt;target-group-arn&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connects the specified &lt;strong&gt;Target Group&lt;/strong&gt; to your &lt;strong&gt;ASG&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Ensures that new instances launched by the ASG are automatically added to the target group for load balancing.
&lt;/li&gt;
&lt;li&gt;Enables health checks so unhealthy instances are replaced automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this step, the Auto Scaling Group will launch two &lt;strong&gt;EC2&lt;/strong&gt; instances (based on the desired capacity you configured earlier).&lt;br&gt;&lt;br&gt;
Once these instances pass their &lt;strong&gt;health checks&lt;/strong&gt;, they’ll be marked as &lt;strong&gt;healthy&lt;/strong&gt;, and the &lt;strong&gt;ALB&lt;/strong&gt; will begin routing traffic to them automatically.&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/create-auto-scaling.webp"&lt;br&gt;
  alt="create-auto-scaling"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;We can also check the Target Group to see that both instances are healthy.&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/target-group.webp"&lt;br&gt;
  alt="target-group"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 5: Testing the Load Balancer
&lt;/h2&gt;

&lt;p&gt;To verify that the &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; is correctly distributing traffic, open the &lt;strong&gt;ALB Dashboard&lt;/strong&gt; in the AWS Management Console and note down the &lt;strong&gt;DNS name&lt;/strong&gt; of your ALB.&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/test-elb.webp"&lt;br&gt;
  alt="test-elb"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;Enter this ALB DNS name in your web browser.&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/browser-elb-1.webp"&lt;br&gt;
  alt="browser-elb-1"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;Now, refresh the browser several times.&lt;/p&gt;

&lt;p&gt;
  src="/static/images/blog/setup-aws-alb-with-auto-scaling-group-asg-using-aws-cli/browser-elb-2.webp"&lt;br&gt;
  alt="browser-elb-2"&lt;br&gt;
  className="mx-auto my-6 w-full rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;As you refresh, you’ll notice that the web page alternates between different &lt;strong&gt;Availability Zones (AZs)&lt;/strong&gt;. This confirms that the &lt;strong&gt;Application Load Balancer&lt;/strong&gt; is intelligently distributing incoming traffic across multiple &lt;strong&gt;EC2 instances&lt;/strong&gt; (targets) in different zones.&lt;br&gt;&lt;br&gt;
Each request may reach a different instance within the &lt;strong&gt;Target Group&lt;/strong&gt;, depending on factors like health status, capacity, and routing algorithm.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In summary&lt;/strong&gt;, this test proves that your &lt;strong&gt;ALB&lt;/strong&gt; and &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; are working together as intended, automatically balancing the load between multiple healthy EC2 instances.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 6: Deleting the Setup
&lt;/h2&gt;

&lt;p&gt;Once you’ve verified that your &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; and &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; are working correctly, it’s good practice to clean up the resources to avoid ongoing AWS charges.&lt;/p&gt;

&lt;p&gt;Run the following commands to delete the setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws elbv2 delete-load-balancer &lt;span class="nt"&gt;--load-balancer-arn&lt;/span&gt; &amp;lt;alb-arn&amp;gt;
aws autoscaling delete-auto-scaling-group &lt;span class="nt"&gt;--auto-scaling-group-name&lt;/span&gt; ASG1 &lt;span class="nt"&gt;--force-delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove the &lt;strong&gt;ALB&lt;/strong&gt; and its associated listeners and target group.
&lt;/li&gt;
&lt;li&gt;Delete the &lt;strong&gt;Auto Scaling Group&lt;/strong&gt; along with any EC2 instances it launched (using the &lt;code&gt;--force-delete&lt;/code&gt; flag ensures immediate deletion).
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may also want to delete other related resources such as the &lt;strong&gt;launch template&lt;/strong&gt;, &lt;strong&gt;target group&lt;/strong&gt;, and &lt;strong&gt;security group&lt;/strong&gt; if they are no longer needed.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;You’ve successfully set up an &lt;strong&gt;AWS Application Load Balancer (ALB)&lt;/strong&gt; integrated with an &lt;strong&gt;Auto Scaling Group (ASG)&lt;/strong&gt; and &lt;strong&gt;Target Group&lt;/strong&gt; — all using the &lt;strong&gt;AWS CLI&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;This architecture automatically adjusts capacity based on demand while distributing traffic evenly across multiple instances, ensuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High availability&lt;/strong&gt; across Availability Zones,
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fault tolerance&lt;/strong&gt; through automatic health checks and scaling, and
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost efficiency&lt;/strong&gt; by running only the resources you need.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this foundation, you can now expand the setup, for example, adding HTTPS with an SSL certificate, customizing health checks, or deploying containerized workloads behind the load balancer.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Kafka Producer-Consumer Using Go and Docker</title>
      <dc:creator>Azam Akram</dc:creator>
      <pubDate>Mon, 08 Jun 2026 23:34:22 +0000</pubDate>
      <link>https://dev.to/azam-akram/building-kafka-producer-consumer-using-go-and-docker-493h</link>
      <guid>https://dev.to/azam-akram/building-kafka-producer-consumer-using-go-and-docker-493h</guid>
      <description>&lt;p&gt;In this blog, we'll walk through the process of building Kafka Producer and Consumer microservices using Go, integrated with Docker.&lt;/p&gt;

&lt;p&gt;We will,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a Go Kafka producer with an HTTP endpoint.&lt;/li&gt;
&lt;li&gt;Build a Go Kafka consumer with retry and dead-letter support.&lt;/li&gt;
&lt;li&gt;Set up Kafka using Docker (KRaft mode — no Zookeeper required).&lt;/li&gt;
&lt;li&gt;Run the application end-to-end.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Microservices" rel="noopener noreferrer"&gt;Microservices&lt;/a&gt; architecture is a popular approach for building scalable and maintainable applications. By breaking down applications into smaller, independent services, developers can enhance flexibility and streamline deployment cycles. We will implement Kafka producer and consumer applications in Go and demonstrate how to run Kafka in a Docker container with Docker Compose to create a seamless microservices environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; is an open-source, statically typed, compiled language designed at Google for simplicity, reliability, and efficiency. It ships with a rich standard library, first-class concurrency primitives (goroutines and channels), and produces single, statically-linked binaries — making it an excellent fit for microservices and containerised workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apache Kafka
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;Kafka&lt;/a&gt; is a distributed streaming platform used to build real-time data pipelines and streaming applications. It allows producers to send messages to topics, which are then consumed by various consumers, making it ideal for event-driven architectures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; is a platform that allows developers to automate the deployment of applications inside lightweight, portable containers. With Docker Compose, you can manage services like Kafka in isolated containers, making it easy to build and maintain microservices architectures.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Go 1.22 or later. If you haven't installed Go yet, &lt;a href="https://dev.to/blog/all-you-need-to-start-go/"&gt;this guide&lt;/a&gt; walks you through the setup.&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;Basic knowledge of Go and Kafka&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Download Code from Github
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/azam-akram/dev-toolkit-go/tree/master/kafka-docker-go" rel="noopener noreferrer"&gt;Download full code&lt;/a&gt; from this github repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;We will create two Go applications:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Kafka Producer&lt;/strong&gt;: Exposes a &lt;code&gt;GET /send&lt;/code&gt; HTTP endpoint that publishes a message to a Kafka topic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kafka Consumer&lt;/strong&gt;: Connects to the topic, processes each message, and retries on failure with dead-letter support.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kafka-docker-go/
├── kafka-producer/
│   ├── go.mod
│   ├── main.go
│   ├── producer.go
│   ├── handler.go
│   └── model.go
├── kafka-consumer/
│   ├── go.mod
│   ├── main.go
│   ├── consumer.go
│   └── model.go
└── docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building and Running the Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Compose for Kafka Setup
&lt;/h3&gt;

&lt;p&gt;Before we start building the Go producer and consumer, we first need a running Kafka instance locally. Instead of manually installing Kafka and managing all its dependencies, we'll use Docker Compose to spin up a ready-to-use environment in a single command.&lt;/p&gt;

&lt;p&gt;We'll be using Apache Kafka running in KRaft mode (Kafka without ZooKeeper). Traditionally, Kafka relied on Apache ZooKeeper for cluster coordination, but modern Kafka versions (3.3+) have introduced KRaft (Kafka Raft metadata mode), which simplifies the architecture by removing ZooKeeper entirely.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;This configuration provisions a single Kafka broker that also acts as a controller (KRaft mode):&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kafka&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache/kafka:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kafka&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9092:9092"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_NODE_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_PROCESS_ROLES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;broker,controller'&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_CONTROLLER_QUORUM_VOTERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1@localhost:9093'&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_LISTENERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PLAINTEXT://:9092,CONTROLLER://:9093'&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_ADVERTISED_LISTENERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PLAINTEXT://localhost:9092'&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_LISTENER_SECURITY_PROTOCOL_MAP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT'&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_CONTROLLER_LISTENER_NAMES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CONTROLLER'&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;KAFKA_PROCESS_ROLES: 'broker,controller'&lt;/code&gt;&lt;br&gt;
Instructs this single container to act simultaneously as both the metadata controller and the message broker, completely eliminating the need for ZooKeeper.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;KAFKA_ADVERTISED_LISTENERS&lt;/code&gt;&lt;br&gt;
Defines the network address and port (&lt;code&gt;localhost:9092&lt;/code&gt;) that external clients, such as your Go producers and consumers, will use to connect to the cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1&lt;/code&gt;&lt;br&gt;
Sets the replication factor for the internal offsets topic to 1. This is ideal for single-node local development, ensuring the cluster stays healthy without requiring peer nodes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To launch your local Kafka environment in detached mode, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;+] up 2/2
 ✔ Network golang-kafka-docker_default  Created                          0.0s
 ✔ Container kafka                      Started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the container is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps
CONTAINER ID   IMAGE                COMMAND                  CREATED         STATUS         PORTS                    NAMES
ae58e1252135   apache/kafka:latest  &lt;span class="s2"&gt;"/__cacert_entrypoin…"&lt;/span&gt;   3 minutes ago   Up 3 minutes   0.0.0.0:9092-&amp;gt;9092/tcp   kafka
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that Kafka is running, let's quickly verify it using the built-in console tools.&lt;/p&gt;

&lt;p&gt;In a terminal, start a Kafka console consumer that reads all messages from the beginning of the topic:&lt;/p&gt;

&lt;p&gt;I wrote following command in git Bash. Git Bash on Windows automatically converts paths starting with / to Windows paths, turning /opt/kafka/bin/... into C:/Program Files/Git/opt/kafka/bin/....&lt;br&gt;
SO I wrote path starting with double slashes, &lt;code&gt;//opt/kafka/bin/kafka-console-consumer.sh&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 &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; kafka //opt/kafka/bin/kafka-console-consumer.sh &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bootstrap-server&lt;/span&gt; localhost:9092 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--topic&lt;/span&gt; demo-kafka-topic &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-beginning&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a second terminal, start a producer to send a test message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; kafka //opt/kafka/bin/kafka-console-producer.sh &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bootstrap-server&lt;/span&gt; localhost:9092 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--topic&lt;/span&gt; demo-kafka-topic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type a message and press Enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from kafka Producer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The consumer terminal will immediately display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from kafka Producer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  src="/static/images/golang-kafka-producer-consumer-with-docker/kafka-producer-consumer-manual-test.webp"&lt;br&gt;
  alt="kafka-producer-consumer-manual-test"&lt;br&gt;
  className="mx-auto my-6 h-5/6 w-5/6 rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Kafka Consumer Service (Go)
&lt;/h2&gt;

&lt;p&gt;Now that our Kafka broker is running, let's build the &lt;strong&gt;consumer service&lt;/strong&gt; that listens to messages from the topic and processes them in real time.&lt;/p&gt;

&lt;p&gt;We'll use &lt;a href="https://github.com/segmentio/kafka-go" rel="noopener noreferrer"&gt;kafka-go&lt;/a&gt; — a pure Go Kafka client that is a light-weight dependency, and gives fine-grained control over offset commits.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;go.mod&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;azam&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;akram&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;consumer&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="m"&gt;1.22&lt;/span&gt;

&lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;segmentio&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.4.51&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Run &lt;code&gt;go mod tidy&lt;/code&gt; after creating this file to download the dependency.&lt;/p&gt;
&lt;h3&gt;
  
  
  Message Model (&lt;code&gt;model.go&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Message&lt;/code&gt; struct is the shared data contract between producer and consumer. It is marshalled to JSON by the producer and unmarshalled back by the consumer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UUID&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"uuid"`&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"from"`&lt;/span&gt;
    &lt;span class="n"&gt;To&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"to"`&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"message"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kafka Consumer (&lt;code&gt;consumer.go&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/segmentio/kafka-go"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="n"&gt;retryDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;KafkaConsumer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;reader&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reader&lt;/span&gt;
    &lt;span class="n"&gt;dlqWriter&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Writer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewKafkaConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groupID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaConsumer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;dlqTopic&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"-dlt"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;KafkaConsumer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReaderConfig&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Brokers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;GroupID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;groupID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;MinBytes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="m"&gt;10e3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;MaxBytes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="m"&gt;10e6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;StartOffset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstOffset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="n"&gt;dlqWriter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Writer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Addr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                   &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                  &lt;span class="n"&gt;dlqTopic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Balancer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeastBytes&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="n"&gt;AllowAutoTopicCreation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&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="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Run fetches messages in a loop until ctx is cancelled.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaConsumer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FetchMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="c"&gt;// clean shutdown on SIGTERM / SIGINT&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR fetch: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR retries exhausted, routing to DLQ — uuid=%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendToDLQ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommitMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR commit: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaConsumer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;processWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lastErr&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lastErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WARN attempt %d/%d failed: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retryDelay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lastErr&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unmarshal: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"================================="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received message:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  UUID:    %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  From:    %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  To:      %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Message: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"================================="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaConsumer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sendToDLQ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dlqWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR DLQ write: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaConsumer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dlqWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&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;Key design decisions explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;kafka.NewReader&lt;/code&gt; with &lt;code&gt;GroupID&lt;/code&gt;&lt;/strong&gt;: registering a &lt;code&gt;GroupID&lt;/code&gt; enables consumer-group semantics — Kafka assigns partitions to group members and tracks committed offsets per group. Multiple consumer instances sharing the same &lt;code&gt;GroupID&lt;/code&gt; can scale horizontally across partitions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;FetchMessage&lt;/code&gt; vs &lt;code&gt;ReadMessage&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;kafka-go&lt;/code&gt; provides two fetch APIs. &lt;code&gt;ReadMessage&lt;/code&gt; automatically commits the offset after each fetch, which risks marking a message as processed even if your handler panics. &lt;code&gt;FetchMessage&lt;/code&gt; leaves the offset uncommitted; we call &lt;code&gt;CommitMessages&lt;/code&gt; only after the message has been fully processed (or routed to the DLQ). This gives us &lt;strong&gt;at-least-once delivery&lt;/strong&gt; semantics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;StartOffset: kafka.FirstOffset&lt;/code&gt;&lt;/strong&gt;: equivalent to &lt;code&gt;auto-offset-reset: earliest&lt;/code&gt; in a Spring Boot config. On first start (no committed offset for the group), the consumer reads from the beginning of the topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;processWithRetry&lt;/code&gt;&lt;/strong&gt;: wraps &lt;code&gt;process&lt;/code&gt; in a simple retry loop — up to &lt;code&gt;maxRetries&lt;/code&gt; attempts with a &lt;code&gt;retryDelay&lt;/code&gt; between each. If a transient error causes &lt;code&gt;process&lt;/code&gt; to fail, the message is retried in place before being sent to the DLQ.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dead-letter topic (&lt;code&gt;-dlt&lt;/code&gt; suffix)&lt;/strong&gt;: if all retry attempts are exhausted, the raw message bytes are forwarded to &lt;code&gt;demo-kafka-topic-dlt&lt;/code&gt;. Failed messages are captured for inspection or manual reprocessing rather than silently dropped — the same pattern as &lt;code&gt;@DltHandler&lt;/code&gt; in Spring Kafka.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Application Entry Point (&lt;code&gt;main.go&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/signal"&lt;/span&gt;
    &lt;span class="s"&gt;"syscall"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;broker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"localhost:9092"&lt;/span&gt;
    &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"demo-kafka-topic"&lt;/span&gt;
    &lt;span class="n"&gt;groupID&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"demo-kafka-consumer"&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"5555"&lt;/span&gt;

    &lt;span class="n"&gt;consumer&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewKafkaConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groupID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Simple health endpoint&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`{"status":"up"}`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Health endpoint on :%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotifyContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Consumer started topic=%s group=%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groupID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Consumer stopped"&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;signal.NotifyContext&lt;/code&gt;&lt;/strong&gt;: creates a context that is cancelled when the process receives &lt;code&gt;SIGINT&lt;/code&gt; (Ctrl+C) or &lt;code&gt;SIGTERM&lt;/code&gt; (e.g. from &lt;code&gt;docker stop&lt;/code&gt;). When the context is cancelled, &lt;code&gt;FetchMessage&lt;/code&gt; returns immediately, allowing the &lt;code&gt;Run&lt;/code&gt; loop to exit cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health goroutine&lt;/strong&gt;: a minimal &lt;code&gt;GET /health&lt;/code&gt; endpoint runs on port 5555 in a background goroutine, so the consumer can be probed by Docker health checks or an orchestrator without interfering with the main consume loop.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Kafka Producer Service (Go)
&lt;/h2&gt;

&lt;p&gt;The producer service exposes a simple REST endpoint. When called, it builds a &lt;code&gt;Message&lt;/code&gt; and publishes it to the Kafka topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;go.mod&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;azam&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;akram&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="m"&gt;1.22&lt;/span&gt;

&lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;segmentio&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.4.47&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Message Model (&lt;code&gt;model.go&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The producer's &lt;code&gt;Message&lt;/code&gt; model mirrors the consumer's exactly, ensuring the JSON wire format is compatible on both sides.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UUID&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"uuid"`&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"from"`&lt;/span&gt;
    &lt;span class="n"&gt;To&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"to"`&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"message"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In a larger system you would extract this into a shared Go module to avoid duplication.&lt;br&gt;
For this self-contained example, both services define their own copy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Kafka Producer (&lt;code&gt;producer.go&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/segmentio/kafka-go"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;KafkaProducer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Writer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewKafkaProducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaProducer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;KafkaProducer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Writer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Addr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                   &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                  &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Balancer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeastBytes&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="n"&gt;RequiredAcks&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;           &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireOne&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;AllowAutoTopicCreation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaProducer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"marshal: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kafka&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"write: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sent  uuid=%s topic=%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaProducer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&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;Key design decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;RequiredAcks: kafka.RequireOne&lt;/code&gt;&lt;/strong&gt;: the broker acknowledges the write once the leader partition has persisted the message. This balances throughput and durability for a single-node development setup. In production, use &lt;code&gt;kafka.RequireAll&lt;/code&gt; to wait for all in-sync replicas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AllowAutoTopicCreation: true&lt;/code&gt;&lt;/strong&gt;: if &lt;code&gt;demo-kafka-topic&lt;/code&gt; does not exist yet, the broker creates it automatically on first write — no need to pre-create topics manually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message key set to UUID&lt;/strong&gt;: using the UUID as the partition key ensures messages with the same UUID always land on the same partition. For random distribution across partitions, omit the key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fmt.Errorf("...: %w", err)&lt;/code&gt;&lt;/strong&gt;: wraps errors with context using the &lt;code&gt;%w&lt;/code&gt; verb so callers can use &lt;code&gt;errors.Is&lt;/code&gt; / &lt;code&gt;errors.As&lt;/code&gt; for structured error handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HTTP Handler (&lt;code&gt;handler.go&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/google/uuid"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;SendHandler&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;producer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;KafkaProducer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SendHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello from Go producer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR send: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to send message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&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="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Message sent!"&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ServeHTTP&lt;/code&gt;&lt;/strong&gt;: implementing the &lt;code&gt;http.Handler&lt;/code&gt; interface directly (rather than a plain &lt;code&gt;func&lt;/code&gt;) lets us inject the &lt;code&gt;*KafkaProducer&lt;/code&gt; dependency cleanly and register the handler with &lt;code&gt;mux.Handle("/send", &amp;amp;SendHandler{...})&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;context.WithTimeout&lt;/code&gt;&lt;/strong&gt;: the send is bound to a 5-second deadline. If the Kafka broker is unavailable, &lt;code&gt;WriteMessages&lt;/code&gt; returns promptly with a deadline-exceeded error rather than hanging the HTTP request indefinitely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;newUUID&lt;/code&gt;&lt;/strong&gt;: generates a RFC 4122 v4 UUID via &lt;code&gt;github.com/google/uuid&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Application Entry Point (&lt;code&gt;main.go&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;broker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"localhost:9092"&lt;/span&gt;
    &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"demo-kafka-topic"&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"4444"&lt;/span&gt;

    &lt;span class="n"&gt;producer&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewKafkaProducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SendHandler&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`{"status":"up"}`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Producer listening on :%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mux&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 producer uses Go's built-in &lt;code&gt;net/http&lt;/code&gt; server, no third-party web framework needed. A &lt;code&gt;GET /health&lt;/code&gt; endpoint is registered alongside &lt;code&gt;/send&lt;/code&gt; so that the service can be health-checked by Docker or an orchestrator.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running the Application End-to-End
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Start Kafka
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 — Start the Consumer
&lt;/h3&gt;

&lt;p&gt;From the &lt;code&gt;kafka-consumer&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod tidy
go run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The consumer starts on port &lt;strong&gt;5555&lt;/strong&gt; and begins listening to &lt;code&gt;demo-kafka-topic&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2026/05/29 10:00:00 Health endpoint on :5555
2026/05/29 10:00:00 Consumer started topic=demo-kafka-topic group=demo-kafka-consumer
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Start the Producer
&lt;/h3&gt;

&lt;p&gt;From the &lt;code&gt;kafka-producer&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod tidy
go run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The producer starts on port &lt;strong&gt;4444&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2026/05/29 10:00:05 Producer listening on :4444
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 — Send a Message
&lt;/h3&gt;

&lt;p&gt;Trigger the producer's REST endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:4444/send
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the response:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 — Observe the Consumer Logs
&lt;/h3&gt;

&lt;p&gt;In the consumer terminal you will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=================================
Received message:
  UUID:    3f2504e0-4f89-11d3-9a0c-0305e82c3301
  From:    Alice
  To:      Bob
  Message: Hello from Go producer
=================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the producer terminal, the send confirmation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2026/05/29 10:00:10 Sent  uuid=3f2504e0-4f89-11d3-9a0c-0305e82c3301 topic=demo-kafka-topic
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  src="/static/images/golang-kafka-producer-consumer-with-docker/kafka-docker-go-e2e-test.png"&lt;br&gt;
  alt="kafka-docker-go-e2e-test"&lt;br&gt;
  className="mx-auto my-6 h-5/6 w-5/6 rounded-lg shadow-lg"&lt;br&gt;
/&amp;gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 6 — Check Health Endpoints
&lt;/h3&gt;

&lt;p&gt;Both services expose a &lt;code&gt;/health&lt;/code&gt; endpoint:&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;# Producer health&lt;/span&gt;
curl http://localhost:4444/health

&lt;span class="c"&gt;# Consumer health&lt;/span&gt;
curl http://localhost:5555/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both return:&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7 — Graceful Shutdown
&lt;/h3&gt;

&lt;p&gt;Press &lt;code&gt;Ctrl+C&lt;/code&gt; in the consumer terminal. The &lt;code&gt;signal.NotifyContext&lt;/code&gt; cancels the context, &lt;code&gt;FetchMessage&lt;/code&gt; returns, and the consumer exits cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2026/05/29 10:01:00 Consumer stopped
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this blog we built two Go microservices that communicate asynchronously through Apache Kafka running in Docker:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs a single-node Kafka broker in KRaft mode (no ZooKeeper)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KafkaProducer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Marshals a &lt;code&gt;Message&lt;/code&gt; to JSON and publishes it to a topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SendHandler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exposes a &lt;code&gt;GET /send&lt;/code&gt; HTTP endpoint to trigger the producer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KafkaConsumer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unmarshals and processes messages with manual offset commits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;processWithRetry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retries failed messages up to 3 times with a 1-second backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sendToDLQ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Routes exhausted messages to a dead-letter topic (&lt;code&gt;-dlt&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key production-ready features we added beyond a minimal example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual offset commit&lt;/strong&gt; — &lt;code&gt;FetchMessage&lt;/code&gt; + &lt;code&gt;CommitMessages&lt;/code&gt; ensures an offset is committed only after successful processing, giving at-least-once delivery semantics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry with backoff&lt;/strong&gt; — transient failures are retried up to 3 times before escalating to the DLQ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead-letter topic&lt;/strong&gt; — messages that exhaust all retries are forwarded to &lt;code&gt;demo-kafka-topic-dlt&lt;/code&gt; for inspection rather than silently dropped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful shutdown&lt;/strong&gt; — &lt;code&gt;signal.NotifyContext&lt;/code&gt; cancels the consume loop on &lt;code&gt;SIGTERM&lt;/code&gt;/&lt;code&gt;SIGINT&lt;/code&gt;, preventing data loss on process stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health endpoints&lt;/strong&gt; — both services expose &lt;code&gt;GET /health&lt;/code&gt; for Docker and orchestrator health checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-driven config&lt;/strong&gt; — all connection parameters (&lt;code&gt;KAFKA_BROKER&lt;/code&gt;, &lt;code&gt;KAFKA_TOPIC&lt;/code&gt;, etc.) are read from environment variables, making both services container-ready out of the box.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>kafka</category>
      <category>docker</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Deploy Your First Go App with Docker and Kubernetes</title>
      <dc:creator>Azam Akram</dc:creator>
      <pubDate>Mon, 08 Jun 2026 23:30:09 +0000</pubDate>
      <link>https://dev.to/azam-akram/deploy-your-first-go-app-with-docker-and-kubernetes-mmk</link>
      <guid>https://dev.to/azam-akram/deploy-your-first-go-app-with-docker-and-kubernetes-mmk</guid>
      <description>&lt;p&gt;This blog is the practical companion to &lt;a href="https://dev.to/blog/kubernetes-fundamentals-docker-and-container-orchestration"&gt;Kubernetes Fundamentals&lt;/a&gt;.&lt;br&gt;
We will build a real Go HTTP server, containerize it with Docker, and deploy it to a local Kubernetes cluster powered by &lt;strong&gt;kind&lt;/strong&gt;.&lt;br&gt;
kind stands for &lt;strong&gt;Kubernetes in Docker&lt;/strong&gt;: it runs Kubernetes nodes as Docker containers, which makes it a convenient way to practise Kubernetes locally without needing a cloud account.&lt;br&gt;
By the end you will have run a rolling update and scaled your application — all from your local machine.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://go.dev/dl/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; 1.21 or later installed.&lt;/li&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;
&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt; installed — the CLI for talking to a Kubernetes cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify everything is working before proceeding:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Create the Go Application
&lt;/h2&gt;

&lt;p&gt;Create a project directory and initialise a Go module:&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;go-k8s-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;go-k8s-demo
go mod init go-k8s-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;main.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="s"&gt;`json:"message"`&lt;/span&gt;
    &lt;span class="n"&gt;Hostname&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="s"&gt;`json:"hostname"`&lt;/span&gt;
    &lt;span class="n"&gt;Version&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="s"&gt;`json:"version"`&lt;/span&gt;
    &lt;span class="n"&gt;Time&lt;/span&gt;     &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt; &lt;span class="s"&gt;`json:"time"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hostname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"Hello from Kubernetes!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Hostname&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"APP_VERSION"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"encode response: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8080"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/healthcheck"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server listening on :%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Listen error: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;os.Hostname()&lt;/code&gt;&lt;/strong&gt; returns the Pod name when running inside Kubernetes. This is handy for verifying which replica handled a request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;APP_VERSION&lt;/code&gt;&lt;/strong&gt; is read from an environment variable so we can inject different values at deploy time without changing the image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/healthcheck&lt;/code&gt;&lt;/strong&gt; is the liveness and readiness probe endpoint Kubernetes will call to check if the container is healthy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test the application locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected 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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello from Kubernetes!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-machine-name"&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;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-02T10:00:00Z"&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;h2&gt;
  
  
  Step 2: Containerize with Docker
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.25.3-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-s -w"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; server .

&lt;span class="c"&gt;# Runtime stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.21&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;-S&lt;/span&gt; app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;-S&lt;/span&gt; app &lt;span class="nt"&gt;-G&lt;/span&gt; app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/server .&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./server"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a &lt;strong&gt;multi-stage build&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first stage (&lt;code&gt;builder&lt;/code&gt;) compiles the binary inside a full Go toolchain image.&lt;/li&gt;
&lt;li&gt;The second stage copies only the compiled binary into a minimal Alpine image.&lt;/li&gt;
&lt;li&gt;The final image is a few megabytes, not hundreds. There is no Go compiler, no source code — only the binary the application needs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CGO_ENABLED=0&lt;/code&gt; produces a fully static binary with no C library dependencies, which is required on the minimal Alpine base.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adduser -S app&lt;/code&gt; runs the process as a non-root user — a standard security baseline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build and test the image locally
&lt;/h3&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; go-k8s-demo:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it locally to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;APP_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1 go-k8s-demo:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response now includes &lt;code&gt;"version": "v1"&lt;/code&gt; because the environment variable was injected at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create a Kubernetes Cluster in Docker Desktop
&lt;/h2&gt;

&lt;p&gt;So far we have only used Docker directly: we built an image and proved the container can run on our machine.&lt;/p&gt;

&lt;p&gt;Kubernetes is the next layer. Instead of starting one container manually with &lt;code&gt;docker run&lt;/code&gt;, we will ask Kubernetes to keep the application running for us. It will create Pods, restart unhealthy containers, expose the app through a Service, and later perform a rolling update.&lt;/p&gt;

&lt;p&gt;For this local tutorial, Docker Desktop can create a small Kubernetes cluster using &lt;strong&gt;kind&lt;/strong&gt;. Because kind runs Kubernetes nodes as Docker containers, your cluster is still local, but it behaves like a real Kubernetes cluster from the point of view of &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Settings → Kubernetes → Create a Kubernetes Cluster&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You will be offered two cluster types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kind&lt;/strong&gt; — recommended. Creates a cluster using &lt;a href="https://kind.sigs.k8s.io/" rel="noopener noreferrer"&gt;kind&lt;/a&gt;. Requires the containerd image store. Locally built images must be explicitly loaded into the cluster with &lt;code&gt;kind load docker-image&lt;/code&gt; before Kubernetes can use them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubeadm&lt;/strong&gt; — creates a single-node cluster with kubeadm. Same requirement: locally built images need to be loaded or pushed to a registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Select &lt;strong&gt;kind&lt;/strong&gt;, leave the node count at &lt;code&gt;1&lt;/code&gt;, and click &lt;strong&gt;Create&lt;/strong&gt;. The first creation pulls cluster components and takes a minute or two. Once the status indicator turns green, verify the cluster is up:&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAME                    STATUS   ROLES           AGE   VERSION
desktop-control-plane   Ready    control-plane   60s   v1.34.3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker Desktop automatically sets the &lt;code&gt;kubectl&lt;/code&gt; context to the new cluster, so no extra context switching is needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Load the image into the kind cluster
&lt;/h3&gt;

&lt;p&gt;Now that the kind cluster exists, we need to make the image available inside it.&lt;/p&gt;

&lt;p&gt;This is a common point of confusion: the image &lt;code&gt;go-k8s-demo:v1&lt;/code&gt; exists in Docker's local image store because we built it with &lt;code&gt;docker build&lt;/code&gt;, but the kind cluster has its own container runtime inside the node container. Kubernetes will not automatically see every image on your host machine. We must explicitly load or import the image before the Deployment can start Pods from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Mac/Linux&lt;/strong&gt; — use the kind CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kind load docker-image go-k8s-demo:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows (Git Bash + Docker Desktop)&lt;/strong&gt; — &lt;code&gt;kind&lt;/code&gt; may be blocked by Windows Application Control policies. Use the following three-step method instead. Docker Desktop's kind node runs inside the &lt;code&gt;desktop-linux&lt;/code&gt; context and the node container is named &lt;code&gt;desktop-control-plane&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Save the image as a tar file in the current directory (avoid &lt;code&gt;/tmp&lt;/code&gt; — Git Bash maps it to a Windows temp path which causes issues):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker save go-k8s-demo:v1 &lt;span class="nt"&gt;-o&lt;/span&gt; go-k8s-demo-v1.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the tar into the kind node container (&lt;code&gt;MSYS_NO_PATHCONV=1&lt;/code&gt; prevents Git Bash from converting the Linux path to a Windows path):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MSYS_NO_PATHCONV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker &lt;span class="nt"&gt;--context&lt;/span&gt; desktop-linux &lt;span class="nb"&gt;cp &lt;/span&gt;go-k8s-demo-v1.tar desktop-control-plane:/go-k8s-demo-v1.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the image into containerd inside the node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MSYS_NO_PATHCONV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker &lt;span class="nt"&gt;--context&lt;/span&gt; desktop-linux &lt;span class="nb"&gt;exec &lt;/span&gt;desktop-control-plane ctr images import /go-k8s-demo-v1.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows (PowerShell or Command Prompt + Docker Desktop)&lt;/strong&gt; — use the same manual import method, but you do not need &lt;code&gt;MSYS_NO_PATHCONV&lt;/code&gt; because PowerShell and Command Prompt do not rewrite Linux-style container paths.&lt;/p&gt;

&lt;p&gt;Save the image as a tar file in the current directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go-k8s-demo:v1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go-k8s-demo-v1.tar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the tar into the kind node container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;desktop-linux&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go-k8s-demo-v1.tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;desktop-control-plane:/go-k8s-demo-v1.tar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the image into containerd inside the node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;desktop-linux&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;desktop-control-plane&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/go-k8s-demo-v1.tar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the image is available.&lt;/p&gt;

&lt;p&gt;Git Bash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MSYS_NO_PATHCONV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker &lt;span class="nt"&gt;--context&lt;/span&gt; desktop-linux &lt;span class="nb"&gt;exec &lt;/span&gt;desktop-control-plane ctr images &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PowerShell or Command Prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;desktop-linux&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;desktop-control-plane&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;docker.io/library/go-k8s-demo:v1&lt;/code&gt; in the list. From here on, Kubernetes can create Pods from that local image without pulling it from Docker Hub or another registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Write Kubernetes Manifests
&lt;/h2&gt;

&lt;p&gt;Create a directory for the manifests:&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;k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;k8s/deployment.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;apps/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;Deployment&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;go-k8s-demo&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&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-k8s-demo&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;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&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;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-k8s-demo&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&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;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-k8s-demo&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;go-k8s-demo&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;go-k8s-demo:v1&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;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;8080&lt;/span&gt;
          &lt;span class="na"&gt;env&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;APP_VERSION&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v1'&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&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;/healthcheck&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;8080&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&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;/healthcheck&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;8080&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&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;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;50m'&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;32Mi'&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;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200m'&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;64Mi'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key settings explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;replicas: 3&lt;/code&gt;&lt;/strong&gt; — Kubernetes will keep exactly 3 Pods running. If one dies, it creates a replacement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;imagePullPolicy: IfNotPresent&lt;/code&gt;&lt;/strong&gt; — Kubernetes uses the image already present on the node and only pulls from a registry if it is missing. Since we manually loaded the image into the kind node's containerd runtime in Step 3, the image is already present and no registry push is needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;livenessProbe&lt;/code&gt;&lt;/strong&gt; — Kubernetes restarts the container if &lt;code&gt;/healthcheck&lt;/code&gt; stops returning 200.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;readinessProbe&lt;/code&gt;&lt;/strong&gt; — Kubernetes only sends traffic to a Pod once &lt;code&gt;/healthcheck&lt;/code&gt; returns 200. During startup or a slow restart, unhealthy Pods are excluded from load balancing automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;resources&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;requests&lt;/code&gt; are what the scheduler uses to find a node with enough room. &lt;code&gt;limits&lt;/code&gt; are the hard cap. Setting both is a production best practice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Service
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;k8s/service.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;go-k8s-demo&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;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-k8s-demo&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;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;80&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;8080&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;NodePort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Service uses &lt;code&gt;NodePort&lt;/code&gt; so we can reach it from outside the cluster. Port &lt;code&gt;80&lt;/code&gt; on the Service maps to port &lt;code&gt;8080&lt;/code&gt; on each Pod. The &lt;code&gt;selector&lt;/code&gt; ties the Service to any Pod labelled &lt;code&gt;app: go-k8s-demo&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Deploy to Kubernetes
&lt;/h2&gt;

&lt;p&gt;Apply both manifests:&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/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that the Deployment and Pods are running:&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 deployments
kubectl get pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAME           READY   UP-TO-DATE   AVAILABLE   AGE
go-k8s-demo    3/3     3            3           20s

NAME                            READY   STATUS    RESTARTS   AGE
go-k8s-demo-6d8b9f5c4d-4xqjk   1/1     Running   0          20s
go-k8s-demo-6d8b9f5c4d-7pnl2   1/1     Running   0          20s
go-k8s-demo-6d8b9f5c4d-kztr9   1/1     Running   0          20s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAME           TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
&lt;/span&gt;&lt;span class="gp"&gt;go-k8s-demo   NodePort   10.96.145.203   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;80:31234/TCP   20s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reach the application
&lt;/h3&gt;

&lt;p&gt;With a kind cluster, &lt;code&gt;NodePort&lt;/code&gt; services are not directly reachable on &lt;code&gt;localhost&lt;/code&gt;. Use &lt;code&gt;kubectl port-forward&lt;/code&gt; to tunnel traffic from your machine to 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 port-forward service/go-k8s-demo 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave that running and in a second terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected 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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello from Kubernetes!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"go-k8s-demo-7c7b7dd9f9-n7q56"&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;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-02T16:36:47Z"&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;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;kubectl port-forward&lt;/code&gt; tunnels to a single Pod, so the &lt;code&gt;hostname&lt;/code&gt; field will be the same across multiple requests. In a real production cluster with a &lt;code&gt;LoadBalancer&lt;/code&gt; service (AWS ELB, GCP LB), traffic would be distributed across all Pods and you would see different hostnames on each request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Perform a Rolling Update
&lt;/h2&gt;

&lt;p&gt;Let's build a second version of the image with a changed message to simulate an application update.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;main.go&lt;/code&gt; — change the message to &lt;code&gt;"Hello from Kubernetes v2!"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Build the new image and load it into the kind cluster using the same method as Step 3:&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; go-k8s-demo:v2 &lt;span class="nb"&gt;.&lt;/span&gt;
docker save go-k8s-demo:v2 &lt;span class="nt"&gt;-o&lt;/span&gt; go-k8s-demo-v2.tar
&lt;span class="nv"&gt;MSYS_NO_PATHCONV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker &lt;span class="nt"&gt;--context&lt;/span&gt; desktop-linux &lt;span class="nb"&gt;cp &lt;/span&gt;go-k8s-demo-v2.tar desktop-control-plane:/go-k8s-demo-v2.tar
&lt;span class="nv"&gt;MSYS_NO_PATHCONV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker &lt;span class="nt"&gt;--context&lt;/span&gt; desktop-linux &lt;span class="nb"&gt;exec &lt;/span&gt;desktop-control-plane ctr images import /go-k8s-demo-v2.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the Deployment to use the new image:&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;set &lt;/span&gt;image deployment/go-k8s-demo go-k8s-demo&lt;span class="o"&gt;=&lt;/span&gt;go-k8s-demo:v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch the rollout happen in real time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout status deployment/go-k8s-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for deployment "go-k8s-demo" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "go-k8s-demo" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "go-k8s-demo" rollout to finish: 1 old replicas are pending termination...
deployment "go-k8s-demo" successfully rolled out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kubernetes terminates old Pods and starts new ones one at a time, keeping at least two running throughout. The application never went offline.&lt;/p&gt;

&lt;p&gt;To roll back to v1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout undo deployment/go-k8s-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Scale the Deployment
&lt;/h2&gt;

&lt;p&gt;Scale to 5 replicas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale deployment go-k8s-demo &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
kubectl get pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scale back down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale deployment go-k8s-demo &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kubernetes sends &lt;code&gt;SIGTERM&lt;/code&gt; to the excess Pods and waits for them to exit before removing them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Inspect and Troubleshoot
&lt;/h2&gt;

&lt;p&gt;Useful commands for day-to-day Kubernetes work:&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;# Describe a Pod — shows events, probe results, resource usage&lt;/span&gt;
kubectl describe pod &amp;lt;pod-name&amp;gt;

&lt;span class="c"&gt;# Stream logs from all Pods matching the label&lt;/span&gt;
kubectl logs &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;go-k8s-demo &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Get logs from a specific Pod&lt;/span&gt;
kubectl logs &amp;lt;pod-name&amp;gt;

&lt;span class="c"&gt;# Open a shell inside a running container&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--&lt;/span&gt; sh

&lt;span class="c"&gt;# Watch all resources in the default namespace&lt;/span&gt;
kubectl get all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Clean Up
&lt;/h2&gt;

&lt;p&gt;When you are done:&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 &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes the Deployment and Service. The kind cluster keeps running in the background. To delete it, go to &lt;strong&gt;Settings → Kubernetes&lt;/strong&gt; in Docker Desktop and delete the cluster, or recreate it with a fresh state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure Summary
&lt;/h2&gt;

&lt;p&gt;The finished project looks 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;go-k8s-demo/
├── main.go
├── go.mod
├── Dockerfile
└── k8s/
    ├── deployment.yaml
    └── service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Covered
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Built a Go HTTP server with a &lt;code&gt;/healthcheck&lt;/code&gt; endpoint.&lt;/li&gt;
&lt;li&gt;Packaged it into a minimal Docker image using a multi-stage build.&lt;/li&gt;
&lt;li&gt;Loaded the image into a local kind Kubernetes cluster running in Docker Desktop.&lt;/li&gt;
&lt;li&gt;Declared the desired state with a Deployment (3 replicas, liveness/readiness probes, resource limits) and a Service (stable DNS, load balancing).&lt;/li&gt;
&lt;li&gt;Performed a zero-downtime rolling update and rollback.&lt;/li&gt;
&lt;li&gt;Scaled replicas up and down with a single command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These same &lt;code&gt;deployment.yaml&lt;/code&gt; and &lt;code&gt;service.yaml&lt;/code&gt; files work unchanged on a production Kubernetes cluster (AWS EKS, GCP GKE, Azure AKS) — the main difference would be pointing the image at a real registry and usually setting &lt;code&gt;imagePullPolicy&lt;/code&gt; to &lt;code&gt;Always&lt;/code&gt; or using a unique version tag for each release. The concepts and workflow you practised here transfer directly.&lt;/p&gt;

&lt;p&gt;You now have a fully functional local development playground running real Kubernetes nodes inside Docker!&lt;/p&gt;

&lt;p&gt;If you want to look at the complete source files or check out more developer tools I've built to simplify cloud-native setups, head over to the original post:&lt;/p&gt;

&lt;p&gt;Read the full guide on &lt;a href="https://www.solutiontoolkit.com" rel="noopener noreferrer"&gt;Solution Toolkit&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>docker</category>
      <category>kubernetes</category>
      <category>containers</category>
    </item>
    <item>
      <title>Top 10 Free Online Tools Every Developer Should Bookmark</title>
      <dc:creator>Azam Akram</dc:creator>
      <pubDate>Mon, 08 Jun 2026 23:21:22 +0000</pubDate>
      <link>https://dev.to/azam-akram/top-10-free-online-tools-every-developer-should-bookmark-120c</link>
      <guid>https://dev.to/azam-akram/top-10-free-online-tools-every-developer-should-bookmark-120c</guid>
      <description>&lt;h1&gt;
  
  
  Top 10 Free Online Tools Every Developer Should Bookmark
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I built these tools after repeatedly running into the same development tasks over and over again. They're free to use, browser-based, and process data locally whenever possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As developers, we spend a surprising amount of time doing small repetitive tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decoding JWTs&lt;/li&gt;
&lt;li&gt;Comparing configuration files&lt;/li&gt;
&lt;li&gt;Converting timestamps&lt;/li&gt;
&lt;li&gt;Generating UUIDs&lt;/li&gt;
&lt;li&gt;Building cron expressions&lt;/li&gt;
&lt;li&gt;Inspecting API payloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these tasks shouldn't require installing software or opening an IDE.&lt;/p&gt;

&lt;p&gt;Here are 10 browser-based tools that save me time almost every week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Common Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File Size Calculator&lt;/td&gt;
&lt;td&gt;Check upload limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gzip Base64 Decoder&lt;/td&gt;
&lt;td&gt;Decode compressed API payloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JWT Debugger&lt;/td&gt;
&lt;td&gt;Inspect authentication tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text Compare&lt;/td&gt;
&lt;td&gt;Compare configs and code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON Escape / Unescape&lt;/td&gt;
&lt;td&gt;Work with encoded JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cron Expression Builder&lt;/td&gt;
&lt;td&gt;Create and validate cron schedules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YAML Formatter&lt;/td&gt;
&lt;td&gt;Validate YAML files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Epoch Converter&lt;/td&gt;
&lt;td&gt;Convert timestamps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UUID Generator&lt;/td&gt;
&lt;td&gt;Generate unique IDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON to Go Struct&lt;/td&gt;
&lt;td&gt;Generate Go models&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  1. File Size Calculator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You need to check how large a file is before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uploading it to S3&lt;/li&gt;
&lt;li&gt;Sending it as an email attachment&lt;/li&gt;
&lt;li&gt;Passing it through an API with payload limits&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;A file size calculator lets you drop a file and instantly view its size in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bytes&lt;/li&gt;
&lt;li&gt;KB&lt;/li&gt;
&lt;li&gt;MB&lt;/li&gt;
&lt;li&gt;GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No uploads required.&lt;/p&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/file-size-calculator" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/file-size-calculator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is surprisingly useful when debugging upload failures caused by hidden size limits.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Gzip Base64 Decoder and Encoder
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You receive an API response that looks 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;H4sIAAAAAAAAA6tWKkktLlGyUlIqS...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of JSON, you're staring at compressed gibberish.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;A Gzip + Base64 decoder can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decode Base64&lt;/li&gt;
&lt;li&gt;Decompress Gzip payloads&lt;/li&gt;
&lt;li&gt;Reveal the original JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/gzip-base64-encoder-decoder" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/gzip-base64-encoder-decoder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've used this frequently when debugging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda responses&lt;/li&gt;
&lt;li&gt;EventBridge events&lt;/li&gt;
&lt;li&gt;Internal microservice communication&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. JWT Debugger
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Authentication suddenly stops working.&lt;/p&gt;

&lt;p&gt;You have a JWT token, but no idea whether:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's expired&lt;/li&gt;
&lt;li&gt;It contains the expected claims&lt;/li&gt;
&lt;li&gt;The wrong signing algorithm was used&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&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;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1788336000&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;A quick inspection immediately reveals whether the token is still valid.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;A JWT debugger decodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Header&lt;/li&gt;
&lt;li&gt;Payload&lt;/li&gt;
&lt;li&gt;Expiration time&lt;/li&gt;
&lt;li&gt;Algorithm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/jwt-debugger" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/jwt-debugger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No libraries or command-line tools required.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Text Compare and Diff Tool
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You have two configuration files that should be identical but aren't behaving the same.&lt;/p&gt;

&lt;p&gt;Common examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes manifests&lt;/li&gt;
&lt;li&gt;Terraform configs&lt;/li&gt;
&lt;li&gt;SQL queries&lt;/li&gt;
&lt;li&gt;YAML files&lt;/li&gt;
&lt;li&gt;JSON payloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;A diff tool highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additions&lt;/li&gt;
&lt;li&gt;Deletions&lt;/li&gt;
&lt;li&gt;Modifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/text-comparison-tool" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/text-comparison-tool&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's often faster than creating a temporary Git repository just to compare two snippets.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. JSON Escape and Unescape Tool
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You encounter JSON that looks like this:&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="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:30}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically valid.&lt;/p&gt;

&lt;p&gt;Practically unreadable.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Convert between:&lt;/p&gt;

&lt;p&gt;Escaped JSON:&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="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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 readable JSON:&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;"John"&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;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/json-escape-unescape" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/json-escape-unescape&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Useful when working with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda payloads&lt;/li&gt;
&lt;li&gt;Elasticsearch queries&lt;/li&gt;
&lt;li&gt;Nested API requests&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Cron Expression Builder
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You need a schedule that runs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every weekday at 9 AM UTC&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But cron syntax is easy to forget.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 9 * * 1-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;9 0 * * 1-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;A cron builder translates schedules into plain English.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 9 * * 1-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every weekday at 09:00 UTC&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/cron-expression-builder" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/cron-expression-builder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard cron&lt;/li&gt;
&lt;li&gt;AWS EventBridge&lt;/li&gt;
&lt;li&gt;Kubernetes CronJobs&lt;/li&gt;
&lt;li&gt;Jenkins&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. YAML Formatter and Validator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;YAML is whitespace-sensitive.&lt;/p&gt;

&lt;p&gt;One incorrect indentation level can break:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes deployments&lt;/li&gt;
&lt;li&gt;GitHub Actions workflows&lt;/li&gt;
&lt;li&gt;Docker Compose files&lt;/li&gt;
&lt;li&gt;Helm charts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;A YAML validator can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Format YAML&lt;/li&gt;
&lt;li&gt;Validate syntax&lt;/li&gt;
&lt;li&gt;Highlight exact error locations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/yaml-formatter" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/yaml-formatter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finding the exact line number saves a surprising amount of frustration.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Epoch and Timestamp Converter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Your logs contain:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You need to know:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What date and time is this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Convert:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unix timestamps → Human-readable dates&lt;/li&gt;
&lt;li&gt;Dates → Unix timestamps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/timestamp-converter" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/timestamp-converter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is one of those tools every backend developer eventually bookmarks.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. UUID Generator and Inspector
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test identifiers&lt;/li&gt;
&lt;li&gt;Seed data&lt;/li&gt;
&lt;li&gt;Temporary resource IDs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or you're working with UUID v7 and want to inspect its embedded timestamp.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Generate and validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UUID v4&lt;/li&gt;
&lt;li&gt;UUID v7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/uuid-generator" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/uuid-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being able to generate multiple UUIDs with one click is especially useful during testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. JSON to Go Struct Converter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You receive a JSON response like this:&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;"user"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&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;"John"&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;Now you need matching Go structs.&lt;/p&gt;

&lt;p&gt;Writing them manually is repetitive and error-prone.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Struct definitions&lt;/li&gt;
&lt;li&gt;Nested structs&lt;/li&gt;
&lt;li&gt;Array types&lt;/li&gt;
&lt;li&gt;JSON tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools/json-to-go-struct" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools/json-to-go-struct&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What would normally take several minutes can often be done in seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honourable Mentions
&lt;/h2&gt;

&lt;p&gt;A few more tools that developers may find useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YAML to JSON Converter&lt;/li&gt;
&lt;li&gt;URL Encoder and Decoder&lt;/li&gt;
&lt;li&gt;Base64 Encoder and Decoder&lt;/li&gt;
&lt;li&gt;Word Frequency Counter&lt;/li&gt;
&lt;li&gt;File Hash Generator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.solutiontoolkit.com/tools" rel="noopener noreferrer"&gt;https://www.solutiontoolkit.com/tools&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The best developer tools aren't always the biggest frameworks or the most advanced IDE plugins.&lt;/p&gt;

&lt;p&gt;Sometimes they're the small utilities that eliminate a repetitive task and save a few minutes every day.&lt;/p&gt;

&lt;p&gt;Those minutes add up.&lt;/p&gt;

&lt;p&gt;What browser-based developer tools do you find yourself using most often?&lt;/p&gt;

&lt;p&gt;I'd love to discover a few new ones for my own bookmarks list.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tooling</category>
      <category>json</category>
      <category>jwt</category>
    </item>
  </channel>
</rss>
