<?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: Kazuya.Y</title>
    <description>The latest articles on DEV Community by Kazuya.Y (@kazuya_york).</description>
    <link>https://dev.to/kazuya_york</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%2F3553722%2F843e4be4-d5b6-432a-9bfe-ea7a2c8bee43.jpg</url>
      <title>DEV Community: Kazuya.Y</title>
      <link>https://dev.to/kazuya_york</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kazuya_york"/>
    <language>en</language>
    <item>
      <title>Automatically Committing Image Tags with Argo CD Image Updater</title>
      <dc:creator>Kazuya.Y</dc:creator>
      <pubDate>Sat, 21 Mar 2026 12:44:45 +0000</pubDate>
      <link>https://dev.to/kazuya_york/automatically-committing-image-tags-with-argo-cd-image-updater-2lio</link>
      <guid>https://dev.to/kazuya_york/automatically-committing-image-tags-with-argo-cd-image-updater-2lio</guid>
      <description>&lt;p&gt;In GitOps workflows using Argo CD, automating container image updates is essential.&lt;/p&gt;

&lt;p&gt;In this article, we will walk through how to set up Argo CD Image Updater in practice based on the following assumptions.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Argo CD is already installed&lt;/li&gt;
&lt;li&gt;Argo CD is connected to GitHub&lt;/li&gt;
&lt;li&gt;Kubernetes is running on EKS&lt;/li&gt;
&lt;li&gt;The container registry is ECR&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Argo CD Image Updater?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://argocd-image-updater.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;Argo CD Image Updater&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Periodically scans &lt;code&gt;kustomization.yaml&lt;/code&gt; or Helm &lt;code&gt;values.yaml&lt;/code&gt; in registered repositories&lt;/li&gt;
&lt;li&gt;Retrieves the latest tags from container registries (e.g., ECR, Docker Hub)&lt;/li&gt;
&lt;li&gt;Compares them with the currently deployed image tags&lt;/li&gt;
&lt;li&gt;If there is a difference, automatically commits the change (or creates a PR) to GitHub&lt;/li&gt;
&lt;li&gt;Argo CD detects the change and performs a rolling update of the Deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deployment Flow: Argo CD Image Updater × Argo CD
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Directory Structure
&lt;/h2&gt;

&lt;p&gt;First, create a Kustomize-based structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ops/kubernetes/eks/argocd-image-updater/
├── base/
└── overlays/
    └── stg/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;①&lt;/strong&gt; Setting up IAM Role and Pod Identity&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;②&lt;/strong&gt; Install Image Updater using Helm Deploy it via Helm from Kustomize.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ops/kubernetes/eks/argocd-image-updater/overlays/stg/kustomization.yaml&lt;/span&gt;

&lt;span class="na"&gt;helmCharts&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;argocd-image-updater&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://argoproj.github.io/argo-helm&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.12.1&lt;/span&gt;
    &lt;span class="na"&gt;releaseName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd-image-updater&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
    &lt;span class="na"&gt;valuesFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;values.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;③&lt;/strong&gt; Configure values.yaml&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;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;logLevel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;info"&lt;/span&gt;

  &lt;span class="na"&gt;registries&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;ECR&lt;/span&gt;
      &lt;span class="na"&gt;api_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://xxx.dkr.ecr.ap-northeast-1.amazonaws.com&lt;/span&gt;
      &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xxx.dkr.ecr.ap-northeast-1.amazonaws.com&lt;/span&gt;
      &lt;span class="na"&gt;insecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ext:/scripts/ecr-login.sh&lt;/span&gt;

  &lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;writeBranch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;develop&lt;/span&gt;
    &lt;span class="na"&gt;commitMessageTemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chore:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;update&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.NewTag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;authorName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Argo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CD&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Updater"&lt;/span&gt;
    &lt;span class="na"&gt;authorEmail&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-email&amp;gt;"&lt;/span&gt;
    &lt;span class="na"&gt;addSignature&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="na"&gt;argocd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;rbac&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;serviceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;argocd-image-updater-sa&lt;/span&gt;

&lt;span class="na"&gt;authScripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;scripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ecr-login.sh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;#!/bin/sh&lt;/span&gt;
      &lt;span class="s"&gt;aws ecr --region "ap-northeast-1" get-authorization-token \&lt;/span&gt;
        &lt;span class="s"&gt;--output text \&lt;/span&gt;
        &lt;span class="s"&gt;--query 'authorizationData[].authorizationToken' \&lt;/span&gt;
      &lt;span class="s"&gt;| base64 -d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Point
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ext:/scripts/ecr-login.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;ECR does not use static credentials like Docker Hub&lt;/li&gt;
&lt;li&gt;→ You must retrieve a temporary token each time&lt;/li&gt;
&lt;li&gt;→ Use a script to fetch the token and pass it to Image Updater&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;④&lt;/strong&gt; Add Annotations to the Application&lt;br&gt;
Image Updater works based on annotations.(v0.x)&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;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;argocd-image-updater.argoproj.io/write-back-method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git&lt;/span&gt;
    &lt;span class="na"&gt;argocd-image-updater.argoproj.io/write-back-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kustomization:/ops/kubernetes/eks/service/overlays/stg"&lt;/span&gt;

    &lt;span class="na"&gt;argocd-image-updater.argoproj.io/image-list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;app-image=xxx.dkr.ecr.ap-northeast-1.amazonaws.com/service-stg&lt;/span&gt;

    &lt;span class="na"&gt;argocd-image-updater.argoproj.io/app-image.update-strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;newest-build&lt;/span&gt;
    &lt;span class="na"&gt;argocd-image-updater.argoproj.io/app-image.kustomize.image-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⑤&lt;/strong&gt; Verification Push a new image to ECR.&lt;/p&gt;

&lt;p&gt;If you see logs like the following in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;argocd-image-updater &amp;gt; Pod details &amp;gt; Logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then the setup is successful.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>infrastructure</category>
      <category>sre</category>
    </item>
    <item>
      <title>How a Misconfigured CloudFront Cache Can Lead to Personal Data Leaks - Understanding and Securing API Caching</title>
      <dc:creator>Kazuya.Y</dc:creator>
      <pubDate>Sun, 26 Oct 2025 04:20:04 +0000</pubDate>
      <link>https://dev.to/kazuya_york/how-a-misconfigured-cloudfront-cache-can-lead-to-personal-data-leaks-understanding-and-securing-m8m</link>
      <guid>https://dev.to/kazuya_york/how-a-misconfigured-cloudfront-cache-can-lead-to-personal-data-leaks-understanding-and-securing-m8m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When using CloudFront, many developers tend to choose the default cache policy &lt;code&gt;Managed-CachingOptimized&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, applying this policy to APIs without fully understanding how it works can lead to serious personal data leaks and other security incidents.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Cache Key
&lt;/h2&gt;

&lt;p&gt;By default, CloudFront creates caches based on the request path.&lt;/p&gt;

&lt;p&gt;In other words, the request path acts as the cache key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Image Requests
&lt;/h3&gt;

&lt;p&gt;User A accesses &lt;code&gt;/images/icon_1.png&lt;/code&gt;&lt;br&gt;
→ CloudFront retrives the object from the origin (e.g., S3) and caches it.&lt;br&gt;
User B accesses &lt;code&gt;/images/icon_1.png&lt;/code&gt;&lt;br&gt;
→ CloudFront returns the cached content (without accessing the origin).&lt;br&gt;
User C accesses &lt;code&gt;/images/icon_2.png&lt;/code&gt;&lt;br&gt;
→ CloudFront fetches the new object from the origin and creates the new cache.&lt;/p&gt;

&lt;p&gt;In short, CloudFront treats the request path as the cache key.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example of the Incident
&lt;/h2&gt;

&lt;p&gt;In 2021, a serious incident occurred at Klarna, a payment service provider based in Sweden.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reference: &lt;a href="https://www.klarna.com/international/press/detailed-incident-report-incorrect-cache-configuration-leading-to-klarna-app-exposing-personal-information/" rel="noopener noreferrer"&gt;Klarna Detailed Incident Report – Incorrect Cache Configuration&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is what happened:&lt;/p&gt;

&lt;p&gt;The CDN cached API responses intended for authenticated users, as a result, personal data was displayed to other users.&lt;/p&gt;

&lt;p&gt;In other words, the response meant for user A was service to user B.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why It Happened
&lt;/h2&gt;

&lt;p&gt;The root cause was that CloudFront's cache key strategy was based solely on the request path.&lt;/p&gt;

&lt;p&gt;Even if an API had a endpoint like &lt;code&gt;/profile&lt;/code&gt; and returned responses for each logged-in user, CloudFront would interpret all those requests as the same path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A → /profile → Response A (cached)  
User B → /profile → CloudFront: “Same path!” → Response A returned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, all users received the personal data of the first user who accessed the endpoint, which led to a serious information leak.&lt;/p&gt;

&lt;h2&gt;
  
  
  Countermeasures
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Countermeasure 1. Disable Caching on the CloudFront Side
&lt;/h3&gt;

&lt;p&gt;You can prevent API responses from being cached by setting the CloudFront cache policy to &lt;code&gt;CachingDisabled&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Countermeasure 2. Set Cache-Control Headers on the Backend
&lt;/h3&gt;

&lt;p&gt;To add an additional layer of protection at the application level,  include this following headers in your API responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache-Control: private, no-cache, no-store, must-revalidate  
Pragma: no-cache  
Expires: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These headers ensure that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CDNs and browsers do not cache the responses&lt;/li&gt;
&lt;li&gt;The origin server is always revalidated before reuse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a result, CloudFront and other intermediaries are forced to fetch a fresh response each time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Countermeasure 3. Completely Separete Static Content and APIs
&lt;/h3&gt;

&lt;p&gt;By hosting static content and APIs on different domains, you can prevent cache configurations from interfering with each other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# For static content  
static.example.com → CloudFront → S3 (caching enabled)  

# For APIs  
api.example.com → ALB / API Gateway → Backend (caching disabled)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup, the communication bacomes cross-origin, therefore you need to configure CORS on the API side.&lt;/p&gt;

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

&lt;p&gt;Cashing on the API side can be useful, but it is also extremely dangerous.&lt;/p&gt;

&lt;p&gt;For APIs that return data for authenticated users, you should completely disable caching.&lt;/p&gt;

&lt;p&gt;If you need to use caching, leverage Redis or Memcached inside your application, and make sure no personal data is stored in the CDN edge cache.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>security</category>
      <category>aws</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Implementing Graceful Shutdown in Go</title>
      <dc:creator>Kazuya.Y</dc:creator>
      <pubDate>Sat, 18 Oct 2025 15:37:41 +0000</pubDate>
      <link>https://dev.to/kazuya_york/implementing-graceful-shutdown-in-go-19el</link>
      <guid>https://dev.to/kazuya_york/implementing-graceful-shutdown-in-go-19el</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;💎 This example demonstrates how to implement graceful shutdown for an asynchronous process that handles heavy email devivery tasks using SQS.&lt;/p&gt;

&lt;p&gt;💎 In the case of an HTTP server, you can simply rely on the standard library &lt;code&gt;Server.Shutdown()&lt;/code&gt; method to safely stop the server. However, since this is an asynchronous queue processor (not an HTTP server), we can't use that mechanism - so we need to implement graceful shutdown manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why graceful shutdown is necessary
&lt;/h2&gt;

&lt;p&gt;When a container is terminated, we want to ensure that some ongoing processes finish properly before the container actually stops.&lt;/p&gt;

&lt;p&gt;SQS workers offen execute logn-running jobs. Some may takes several minutes, even close to 1 hour.&lt;/p&gt;

&lt;p&gt;Without graceful shutdown, if a deployment happens while a worker is still processing a message,  the process might be killed in the middle of the execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to implement
&lt;/h2&gt;

&lt;p&gt;We can handle graceful shutdown by responding to system signals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SIGTERM&lt;/code&gt; - sent when the container is terminated (e.g., during a deployment)&lt;/li&gt;
&lt;li&gt; &lt;code&gt;SIGINT&lt;/code&gt; - sent when the process is manually stopped (e.g., Ctrl+C)
If you consider to handle these signals, you can ensure that no matter when a deployment happens, the process is always wait for all ongoing tasks to finish before exiting — allowing the system to shud down safely and gracefully.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Implementation
&lt;/h2&gt;

&lt;p&gt;I will show the flows and code below.🚀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Startup
 ↓ 
Create SQS session
 ↓
ReceiveMessage in a for loop
 ↓
Process each message in a goroutine
 ↓
SIGINT / SIGTERM received → ctx.Cancel()
 ↓
Exit loop &amp;amp; wait for all wg to finish
 ↓
Log output and perform graceful shutdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// main.go&lt;/span&gt;
&lt;span class="n"&gt;sigChan&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&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;Signal&lt;/span&gt;&lt;span class="p"&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;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sigChan&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;SIGINT&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="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;WithCancel&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="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;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;

&lt;span class="c"&gt;// Listen for signals in the goroutine&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="err"&gt;　&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;sigChan&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="err"&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;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}()&lt;/span&gt;

&lt;span class="c"&gt;// Initialize AWS session&lt;/span&gt;
&lt;span class="n"&gt;session&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;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&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="s"&gt;"your-region"&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="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wrap&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="s"&gt;"session.NewSession"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sqs&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="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Define SQS receive message configuration&lt;/span&gt;
&lt;span class="n"&gt;receiveMessageInput&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;sqs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReceiveMessageInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xxx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="n"&gt;MaxNumberOfMessages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;yyy&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="n"&gt;MessageAttributeNames&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;zzz&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="err"&gt;　&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&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;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;　　&lt;/span&gt;&lt;span class="c"&gt;// Exit the loop if context is cancelled&lt;/span&gt;
&lt;span class="err"&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="err"&gt;　&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;　　&lt;/span&gt;&lt;span class="n"&gt;result&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;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReceiveMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receiveMessageInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&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="err"&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="m"&gt;1&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="c"&gt;// Retry after a short delay&lt;/span&gt;
&lt;span class="err"&gt;　　　&lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;
  &lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;　　&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;
  &lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;　　&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&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="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Messages&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;　　　&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sqs&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="c"&gt;// Process received messages&lt;/span&gt;
   &lt;span class="err"&gt;　&lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;　　&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&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;



</description>
      <category>go</category>
      <category>aws</category>
      <category>sre</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
