<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bartek Gałęzowski</title>
    <description>The latest articles on Forem by Bartek Gałęzowski (@galezovsky).</description>
    <link>https://forem.com/galezovsky</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%2F955937%2F55ec2419-e5d8-4018-9879-4af5a3b6cf71.jpeg</url>
      <title>Forem: Bartek Gałęzowski</title>
      <link>https://forem.com/galezovsky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/galezovsky"/>
    <language>en</language>
    <item>
      <title>Secure RDS Access Without Bastion Hosts: Using ECS Containers and SSM</title>
      <dc:creator>Bartek Gałęzowski</dc:creator>
      <pubDate>Wed, 25 Feb 2026 08:00:00 +0000</pubDate>
      <link>https://forem.com/u11d/secure-rds-access-without-bastion-hosts-using-ecs-containers-and-ssm-43pe</link>
      <guid>https://forem.com/u11d/secure-rds-access-without-bastion-hosts-using-ecs-containers-and-ssm-43pe</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Accessing RDS databases in production environments presents a common security challenge. Direct internet access to databases is a significant security risk, so most organizations place their RDS instances in private subnets without public endpoints. But how do you access these databases for debugging, data analysis, or administrative tasks?&lt;/p&gt;

&lt;p&gt;Traditional approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bastion hosts&lt;/strong&gt;: Requires maintaining and securing additional EC2 instances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPN connections&lt;/strong&gt;: Complex setup and ongoing maintenance overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH tunneling&lt;/strong&gt;: Still requires a jump server with SSH access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's a more elegant solution: leveraging your existing ECS containers as secure tunnels to your RDS databases using AWS Systems Manager (SSM) Session Manager.&lt;/p&gt;

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

&lt;p&gt;This script creates a secure tunnel from your local machine to an RDS database through a running ECS container, using SSM Session Manager for the connection. No SSH keys, no bastion hosts, no exposed ports—just IAM-based authentication and encrypted connections.&lt;/p&gt;

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

&lt;p&gt;Before using this script, ensure you have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; installed and configured with appropriate credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Manager Plugin&lt;/strong&gt; for AWS CLI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS Task Role&lt;/strong&gt; with permissions to use SSM:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"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;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="s2"&gt;"ssmmessages:CreateControlChannel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="s2"&gt;"ssmmessages:CreateDataChannel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="s2"&gt;"ssmmessages:OpenControlChannel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="s2"&gt;"ssmmessages:OpenDataChannel"&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;"Resource"&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="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;ol&gt;
&lt;li&gt;
&lt;strong&gt;ECS Exec enabled&lt;/strong&gt; on your service (can be enabled with &lt;code&gt;aws ecs update-service --cluster &amp;lt;cluster&amp;gt; --service &amp;lt;service&amp;gt; --enable-execute-command --force-new-deployment&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The script performs the following operations:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Task Discovery
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TASK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecs list-tasks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLUSTER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--desired-status&lt;/span&gt; RUNNING &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'taskArns[0]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finds the first running task in the specified ECS service.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Container Runtime ID Retrieval
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONTAINER_RUNTIME_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecs describe-tasks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLUSTER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tasks&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TASK_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'tasks[0].containers[?name==`'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'`].runtimeId | [0]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Port Forwarding Session
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm start-session &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="s2"&gt;"ecs:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_RUNTIME_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--document-name&lt;/span&gt; AWS-StartPortForwardingSessionToRemoteHost &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="s1"&gt;'{"host":["$DB_HOST"],"portNumber":["5432"], "localPortNumber":["$LOCAL_PORT"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Establishes a secure tunnel through the ECS container to the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Usage&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eu&lt;/span&gt;

usage&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;CLUSTER&amp;gt; &amp;lt;SERVICE_NAME&amp;gt; &amp;lt;DB_HOST&amp;gt; &amp;lt;LOCAL_PORT&amp;gt; &amp;lt;REGION&amp;gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  cluster: ECS cluster name"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  service: ECS service name"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  db_host: Database host"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  local_port: Local port to forward"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  region:  AWS region (e.g., us-east-2)"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-ne&lt;/span&gt; 5 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: Expected 5 arguments, got $#"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  usage
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;LOCAL_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$5&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

get_first_task_id&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;FIRST_TASK_ID
  &lt;span class="nv"&gt;FIRST_TASK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecs list-tasks &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLUSTER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--service-name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--desired-status&lt;/span&gt; RUNNING &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'taskArns[0]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/.*\///'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FIRST_TASK_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"None"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FIRST_TASK_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: No running tasks found for service '&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;' in cluster '&lt;/span&gt;&lt;span class="nv"&gt;$CLUSTER&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FIRST_TASK_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;TASK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_first_task_id&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;CONTAINER_RUNTIME_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecs describe-tasks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLUSTER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tasks&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TASK_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'tasks[0].containers[?name==`'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'`].runtimeId | [0]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ecs:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_RUNTIME_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

aws ssm start-session &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--document-name&lt;/span&gt; AWS-StartPortForwardingSessionToRemoteHost &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;host&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="nv"&gt;$DB_HOST&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;portNumber&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;5432&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;localPortNumber&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="nv"&gt;$LOCAL_PORT&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="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Save the script as &lt;code&gt;ecs-db-tunnel.sh&lt;/code&gt; and make it executable:&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;chmod&lt;/span&gt; +x ecs-db-tunnel.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script with the required parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./ecs-db-tunnel.sh &amp;lt;CLUSTER&amp;gt; &amp;lt;SERVICE_NAME&amp;gt; &amp;lt;DB_HOST&amp;gt; &amp;lt;LOCAL_PORT&amp;gt; &amp;lt;REGION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./ecs-db-tunnel.sh &lt;span class="se"&gt;\&lt;/span&gt;
  production-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  api-service &lt;span class="se"&gt;\&lt;/span&gt;
  mydb.c9akciq32.us-east-2.rds.amazonaws.com &lt;span class="se"&gt;\&lt;/span&gt;
  5432 &lt;span class="se"&gt;\&lt;/span&gt;
  us-east-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once connected, you can access the database from your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-U&lt;/span&gt; dbuser &lt;span class="nt"&gt;-d&lt;/span&gt; mydb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or using other tools such as &lt;a href="https://dbeaver.io/" rel="noopener noreferrer"&gt;DBeaver&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Key Benefits&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. No Infrastructure Overhead&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;No need to maintain bastion hosts or VPN servers. You leverage existing ECS containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Enhanced Security&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No SSH keys to manage or rotate&lt;/li&gt;
&lt;li&gt;No exposed ports or public endpoints&lt;/li&gt;
&lt;li&gt;IAM-based authentication and authorization&lt;/li&gt;
&lt;li&gt;All traffic encrypted through SSM&lt;/li&gt;
&lt;li&gt;Audit trail through CloudTrail&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Zero Configuration&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your ECS service is already running with ECS Exec enabled, you're ready to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Cost Effective&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;No additional EC2 instances to run. Session Manager has no additional cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. Temporary Access&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Connection exists only while the script is running—perfect for adhoc administrative tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Security Considerations&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Network Security&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This approach works because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ECS container is in the same VPC as the RDS instance&lt;/li&gt;
&lt;li&gt;The container's security group allows outbound connections to the RDS security group&lt;/li&gt;
&lt;li&gt;The RDS security group permits inbound connections from the ECS container's security group&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Access Control&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Control who can establish tunnels using IAM policies:&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;"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;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ecs:ListTasks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ecs:DescribeTasks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssm:StartSession"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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;"Condition"&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;"StringEquals"&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;"aws:RequestedRegion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-2"&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;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Troubleshooting&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;No running tasks found&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Ensure the ECS service has at least one running task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecs list-tasks &lt;span class="nt"&gt;--cluster&lt;/span&gt; &amp;lt;cluster&amp;gt; &lt;span class="nt"&gt;--service-name&lt;/span&gt; &amp;lt;service&amp;gt; &lt;span class="nt"&gt;--desired-status&lt;/span&gt; RUNNING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Session Manager plugin not found&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Install the Session Manager plugin following &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Connection refused&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RDS security group allows inbound from ECS container security group&lt;/li&gt;
&lt;li&gt;Database endpoint is correct&lt;/li&gt;
&lt;li&gt;ECS task has network connectivity to RDS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;ECS Exec not enabled&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Enable it on your service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecs update-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &amp;lt;cluster&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service&lt;/span&gt; &amp;lt;service&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-execute-command&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--force-new-deployment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Using ECS containers as secure tunnels to RDS databases is an elegant solution that leverages existing infrastructure while maintaining strong security posture. This approach eliminates the need for bastion hosts, reduces attack surface, and provides auditable, temporary access to private databases.&lt;/p&gt;

&lt;p&gt;The script demonstrates that sometimes the best security solutions are those that work with your existing architecture rather than adding more complexity on top of it.&lt;/p&gt;

</description>
      <category>rds</category>
      <category>aws</category>
      <category>ssm</category>
      <category>ecs</category>
    </item>
    <item>
      <title>How to Disable HTTP Request Logs in Medusa v1 and v2?</title>
      <dc:creator>Bartek Gałęzowski</dc:creator>
      <pubDate>Tue, 11 Nov 2025 23:00:00 +0000</pubDate>
      <link>https://forem.com/u11d/how-to-disable-http-request-logs-in-medusa-v1-and-v2-4j05</link>
      <guid>https://forem.com/u11d/how-to-disable-http-request-logs-in-medusa-v1-and-v2-4j05</guid>
      <description>&lt;p&gt;Excessive HTTP request logs in Medusa.js can slow down your e-commerce backend and clutter production logs. While detailed request logging is valuable in development, it becomes a performance bottleneck in live environments with high traffic. In this guide, you’ll learn how to &lt;strong&gt;disable or manage request logs in Medusa.js v1 and v2&lt;/strong&gt;, improve server efficiency, and keep only the essential logs that truly matter in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Control HTTP Request Logs?
&lt;/h2&gt;

&lt;p&gt;HTTP request logs in Medusa.js can become overwhelming, especially for e-commerce stores with significant traffic. Every API call, webhook, and admin request generates log entries. While useful for debugging, these logs can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Degrade performance&lt;/strong&gt; - Writing thousands of log entries consumes CPU and I/O resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce log readability&lt;/strong&gt; - Important error messages get buried in routine request logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increase storage costs&lt;/strong&gt; - Log aggregation services charge based on volume&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complicate monitoring&lt;/strong&gt; - Alert systems struggle to identify real issues among noise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For production stores handling hundreds or thousands of requests per minute, controlling request logs is not just a convenience—it's a necessity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disabling Request Logs in Medusa.js v1
&lt;/h2&gt;

&lt;p&gt;Medusa v1 doesn't provide a built-in configuration option to disable HTTP request logs. While Medusa has moved to v2 and only provides security updates for v1, many production stores still run on v1. The lack of log control can be a significant operational challenge.&lt;/p&gt;

&lt;p&gt;The only reliable solution is to patch the Medusa source code using &lt;code&gt;patch-package&lt;/code&gt;. This approach modifies how Medusa's HTTP logger behaves, allowing you to conditionally disable request logs based on the &lt;code&gt;LOG_LEVEL&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A Medusa v1 project&lt;/li&gt;
&lt;li&gt;Node.js and npm/yarn installed&lt;/li&gt;
&lt;li&gt;Access to modify your project's dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Install patch-package
&lt;/h3&gt;

&lt;p&gt;First, install &lt;code&gt;patch-package&lt;/code&gt; as a development dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; patch-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; patch-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add postinstall Script
&lt;/h3&gt;

&lt;p&gt;Update your &lt;code&gt;package.json&lt;/code&gt; to run &lt;code&gt;patch-package&lt;/code&gt; after installing dependencies:&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;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postinstall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"patch-package"&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;This ensures your patches are applied automatically whenever you run &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;yarn install&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Locate the HTTP Logger File
&lt;/h3&gt;

&lt;p&gt;Navigate to your &lt;code&gt;node_modules&lt;/code&gt; directory and find the Medusa core HTTP logger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/@medusajs/medusa/dist/loaders/logger.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file initializes the &lt;a href="https://github.com/expressjs/morgan" rel="noopener noreferrer"&gt;Morgan&lt;/a&gt; HTTP request logger that generates all the request logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Modify the Logger Configuration
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;node_modules/@medusajs/medusa/dist/loaders/express.js&lt;/code&gt; and locate the Morgan middleware configuration. It typically looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace it with a conditional configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LOG_LEVEL&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;debug&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This modification ensures that HTTP request logs appear when &lt;code&gt;LOG_LEVEL=debug&lt;/code&gt; is explicitly set.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Create the Patch
&lt;/h3&gt;

&lt;p&gt;After modifying the file, create a patch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx patch-package @medusajs/medusa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates a patch file in &lt;code&gt;patches/@medusajs+medusa+[version].patch&lt;/code&gt; that contains your modifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Configure Your Environment
&lt;/h3&gt;

&lt;p&gt;In your production environment, ensure &lt;code&gt;LOG_LEVEL&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; set to &lt;code&gt;debug&lt;/code&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Verification
&lt;/h3&gt;

&lt;p&gt;Restart your Medusa server. In production mode, you should no longer see HTTP request logs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[http]: GET /store/products 200 45ms
[http]: POST /store/cart 201 120ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These logs will only appear when running with &lt;code&gt;LOG_LEVEL=debug&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that &lt;code&gt;LOG_LEVEL&lt;/code&gt; cannot be set using &lt;code&gt;.env&lt;/code&gt; file, because this environment variable must be set before starting the server, as a system environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Disabling Request Logs in Medusa.js v2
&lt;/h2&gt;

&lt;p&gt;Good news! Medusa v2 includes built-in support for controlling HTTP request logs without requiring patches. The logging system has been completely redesigned with proper configuration options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the Logging Configuration
&lt;/h3&gt;

&lt;p&gt;Medusa v2 provides a comprehensive logging configuration system documented in the &lt;a href="https://docs.medusajs.com/learn/debugging-and-testing/logging#logging-configurations" rel="noopener noreferrer"&gt;official Medusa v2 logging guide&lt;/a&gt;.&lt;br&gt;
The available log levels, from lowest to highest levels, are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;silly&lt;/li&gt;
&lt;li&gt;debug&lt;/li&gt;
&lt;li&gt;verbose&lt;/li&gt;
&lt;li&gt;http (default, meaning only HTTP requests are logged)&lt;/li&gt;
&lt;li&gt;info&lt;/li&gt;
&lt;li&gt;warn&lt;/li&gt;
&lt;li&gt;error&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to disable HTTP request logs specify your own &lt;code&gt;LOG_LEVEL&lt;/code&gt; env variable without &lt;code&gt;http&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;p&gt;Regardless of which Medusa version you're using, follow these logging best practices:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Use Environment-Specific Configurations
&lt;/h3&gt;

&lt;p&gt;Always differentiate between development, staging, and production logging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development&lt;/strong&gt;: Enable all logs including HTTP requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staging&lt;/strong&gt;: Enable moderate logging to catch issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production&lt;/strong&gt;: Disable HTTP logs, keep error and warning logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Monitor Performance Impact
&lt;/h3&gt;

&lt;p&gt;Before and after implementing log controls, monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU usage during peak traffic&lt;/li&gt;
&lt;li&gt;I/O wait times&lt;/li&gt;
&lt;li&gt;Log storage consumption&lt;/li&gt;
&lt;li&gt;Application response times&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Keep Critical Logs
&lt;/h3&gt;

&lt;p&gt;Even when disabling HTTP logs, maintain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error logs (always enabled)&lt;/li&gt;
&lt;li&gt;Warning logs (business logic issues)&lt;/li&gt;
&lt;li&gt;Security-related logs (authentication failures, suspicious activity)&lt;/li&gt;
&lt;li&gt;Transaction logs (payment processing, order creation)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Implement Log Rotation
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Patch Not Applied (v1)
&lt;/h3&gt;

&lt;p&gt;If your patch isn't working:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify &lt;code&gt;postinstall&lt;/code&gt; script is in &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Delete &lt;code&gt;node_modules&lt;/code&gt; and reinstall: &lt;code&gt;rm -rf node_modules &amp;amp;&amp;amp; npm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check patch file exists: &lt;code&gt;ls patches/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Manually apply: &lt;code&gt;npx patch-package @medusajs/medusa&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Logs Still Appearing (v2)
&lt;/h3&gt;

&lt;p&gt;If logs persist in Medusa v2:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check environment variables are loaded: &lt;code&gt;echo $LOG_LEVEL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check for custom middleware that might add logging&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Controlling request logs is crucial for high-traffic Medusa.js e-commerce stores. While Medusa v1 requires a workaround using &lt;code&gt;patch-package&lt;/code&gt;, Medusa v2 provides built-in configuration options that make log management straightforward.&lt;/p&gt;

&lt;p&gt;For v1 projects, the patch approach is currently the only reliable solution, despite v1 being in maintenance mode with security-only updates. Many production stores still depend on v1, making this patch an essential operational improvement.&lt;/p&gt;

&lt;p&gt;For new projects or those planning to migrate, Medusa v2 offers a more robust and maintainable logging system that eliminates the need for patches.&lt;/p&gt;

&lt;p&gt;By implementing proper log control, you'll improve application performance, reduce operational costs, and make your logs more actionable for debugging real issues. Whether you're on v1 or v2, taking control of your logging strategy is an investment that pays dividends in operational efficiency.&lt;/p&gt;

</description>
      <category>medusa</category>
      <category>morgan</category>
      <category>pathpackage</category>
    </item>
    <item>
      <title>E-commerce Load &amp; Stress Testing with k6 on AWS Fargate</title>
      <dc:creator>Bartek Gałęzowski</dc:creator>
      <pubDate>Wed, 10 Sep 2025 08:15:42 +0000</pubDate>
      <link>https://forem.com/u11d/e-commerce-load-stress-testing-with-k6-on-aws-fargate-70l</link>
      <guid>https://forem.com/u11d/e-commerce-load-stress-testing-with-k6-on-aws-fargate-70l</guid>
      <description>&lt;h2&gt;
  
  
  Why E-commerce Load Testing is Critical?
&lt;/h2&gt;

&lt;p&gt;E-commerce platforms face unique performance challenges that can make or break business success. This is exactly why &lt;strong&gt;e-commerce load testing is critical,&lt;/strong&gt; it ensures your store can deliver a fast, reliable experience no matter how many users are browsing or buying at once. Unlike traditional websites, online stores must seamlessly handle complex user journeys including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Product searches and filtering&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-time inventory checks&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shopping cart management&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payment processing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Order fulfillment workflows&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stakes are incredibly high: &lt;strong&gt;a single second of delay results in a 7% reduction in conversions&lt;/strong&gt;, and during peak traffic events like Black Friday or flash sales, even minor performance issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scale Challenge in E-Commerce Load Testing
&lt;/h2&gt;

&lt;p&gt;In e-commerce, the question isn’t simply whether your platform works - it’s whether it can perform flawlessly when hundreds or thousands of shoppers are online at the same time. &lt;strong&gt;E-commerce load testing&lt;/strong&gt; ensures your site remains fast, stable, and responsive under real-world pressure. Your testing strategy should match your business stage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Startups&lt;/strong&gt;: Validate platform stability before your first major marketing campaigns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Growing businesses&lt;/strong&gt;: Guarantee smooth scaling during seasonal peaks and viral traffic spikes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise teams&lt;/strong&gt;: Simulate millions of concurrent users across multiple AWS regions for global coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why E2E Tests Aren’t Enough?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Traditional &lt;strong&gt;end-to-end (E2E) testing&lt;/strong&gt; works well for verifying individual features, but it falls short when it comes to &lt;strong&gt;scalability testing&lt;/strong&gt;. While E2E automation can confirm that checkout or search flows work for single users, it doesn’t reveal how your site performs under thousands of simultaneous transactions.&lt;/p&gt;

&lt;p&gt;Imagine you’ve built the smoothest checkout process possible — perfectly tested with automation scripts. You might even run 2–4 of these flows in parallel, but beyond that, your local machine simply can’t keep up. Most development laptops max out at generating traffic for &lt;strong&gt;50–100 virtual users (VUs)&lt;/strong&gt;. Realistic &lt;strong&gt;e-commerce load testing&lt;/strong&gt; often requires thousands.&lt;br&gt;
The main resource limitations are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU bottlenecks&lt;/strong&gt; on local machines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory constraints&lt;/strong&gt; during peak simulations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network bandwidth limits&lt;/strong&gt; when testing at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without distributed infrastructure, it’s impossible to replicate real-world shopping surges.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;distributed load testing with k6 and AWS ECS Fargate&lt;/strong&gt; becomes mission-critical. By matching test design to your unique traffic patterns and infrastructure, you can identify weaknesses before they cost you sales.&lt;/p&gt;
&lt;h3&gt;
  
  
  But what is k6?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Grafana k6&lt;/strong&gt; is an open-source load testing tool for developers, designed to test the performance and reliability of APIs, microservices, and websites. It uses JavaScript for scripting, is lightweight, and focuses on automation, CI/CD integration, and developer-friendly workflows. They also offers &lt;strong&gt;k6 Cloud&lt;/strong&gt; a managed SaaS solution that removes infrastructure headaches while delivering enterprise-scale load testing. You can run the same k6 scripts locally and in the cloud, making it convenient for teams already using k6.&lt;br&gt;
However, the trade-offs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High costs&lt;/strong&gt; for large-scale or frequent tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage limits&lt;/strong&gt; depending on your subscription&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced customization&lt;/strong&gt; compared to self-managed infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many e-commerce teams, these limitations make a self-hosted or hybrid model more cost-effective in the long term.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;k6 and AWS ECS Fargate for Distributed E-commerce Load Testing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A more flexible approach is to combine &lt;strong&gt;k6&lt;/strong&gt; with &lt;strong&gt;AWS ECS (Elastic Container Service) and Fargate&lt;/strong&gt; to create a distributed, scalable, and cost-efficient load testing setup. Each container acts as an independent load generator, so you can spin up dozens — or even hundreds — of instances across AWS regions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why ECS Fargate Works for Load Testing at Scale&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless architecture&lt;/strong&gt; — no EC2 or cluster management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Massive scalability&lt;/strong&gt; — run hundreds of containers simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost control&lt;/strong&gt; — pay only for the resource used during testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global reach&lt;/strong&gt; — simulate traffic from multiple AWS regions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container isolation&lt;/strong&gt; — each load generator runs independently without affecting others&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Prerequisites for Deployment&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before building your &lt;strong&gt;distributed load testing&lt;/strong&gt; environment, you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI configured with IAM permissions&lt;/li&gt;
&lt;li&gt;Docker installed locally&lt;/li&gt;
&lt;li&gt;Basic understanding of ECS concepts&lt;/li&gt;
&lt;li&gt;An Amazon ECR (Elastic Container Registry) repository&lt;/li&gt;
&lt;li&gt;Prepared k6 test scripts (e.g., &lt;code&gt;load-test.js&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step-by-Step Implementation Guide
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Creating the Optimized Docker Image
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; that builds a custom k6 image with additional capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.24-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;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
&lt;span class="k"&gt;RUN &lt;/span&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;go.k6.io/xk6/cmd/xk6@latest &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; xk6 build &lt;span class="nt"&gt;--with&lt;/span&gt; github.com/LeonAdato/xk6-output-statsd

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; grafana/k6:0.57.0-with-browser&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /go/k6 /usr/bin/k6&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; K6_STATSD_ENABLE_TAGS=true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["run", "-o", "output-statsd", "--include-system-env-vars", "--compatibility-mode=experimental_enhanced", "load-test.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important Note&lt;/strong&gt;: This configuration includes &lt;a href="https://github.com/LeonAdato/xk6-output-statsd" rel="noopener noreferrer"&gt;xk6-output-statsd&lt;/a&gt; for real-time output of k6 test metrics to a &lt;a href="https://github.com/statsd/statsd" rel="noopener noreferrer"&gt;StatsD&lt;/a&gt; service, enabling advanced monitoring capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Building and Pushing the Docker Image
&lt;/h3&gt;

&lt;p&gt;Build and deploy your Docker image to AWS ECR:&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;# Build the image (add --platform=linux/amd64 for Apple Silicon Macs)&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; k6-ecommerce-load-test &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Tag for ECR&lt;/span&gt;
docker tag k6-ecommerce-load-test:latest &lt;span class="o"&gt;[&lt;/span&gt;your-ecr-uri]:[your-tag]

&lt;span class="c"&gt;# Authenticate with ECR&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;your-region] | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;your-ecr-uri]

&lt;span class="c"&gt;# Push to ECR&lt;/span&gt;
docker push &lt;span class="o"&gt;[&lt;/span&gt;your-ecr-uri]:[your-tag]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Automated Build Script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For convenience, use this automated build script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Usage: ./build-image.sh &amp;lt;ecr-uri&amp;gt; &amp;lt;tag&amp;gt; &amp;lt;region&amp;gt;&lt;/span&gt;

&lt;span class="nv"&gt;ECR_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;ecr-uri&amp;gt; &amp;lt;tag&amp;gt; &amp;lt;region&amp;gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building k6 load test image..."&lt;/span&gt;
docker build &lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; k6-ecommerce-load-test &lt;span class="nb"&gt;.&lt;/span&gt;
docker tag k6-ecommerce-load-test:latest &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pushing to ECR..."&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
docker push &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Image successfully pushed to ECR!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to find your image in ECR registry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oiu4dmoe3dtqjrzzzfj.webp" 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%2F3oiu4dmoe3dtqjrzzzfj.webp" alt=" " width="800" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: ECS Task Definition Configuration
&lt;/h3&gt;

&lt;p&gt;Create an ECS task definition file (&lt;code&gt;task-definition.json&lt;/code&gt;) for your k6 load test:&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;"family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"k6-ecommerce-load-test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"networkMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"awsvpc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requiresCompatibilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cpu"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"executionRoleArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::YOUR-ACCOUNT:role/YOUR-EXECUTION-ROLE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"taskRoleArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::YOUR-ACCOUNT:role/YOUR-TASK-ROLE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"containerDefinitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;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;"k6-load-test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image"&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-ecr-uri]:[tag-of-your-image]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"essential"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;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;"RUNTIME_ENV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example"&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;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;"OTHER_ENV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&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;span class="nl"&gt;"logConfiguration"&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;"logDriver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"awslogs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&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;"awslogs-group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/ecs/k6-load-test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"awslogs-region"&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-aws-region]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"awslogs-stream-prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ecs"&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;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to specifying the AWS region, ECR URI, image tag, and your runtime envs, you must also configure the essential IAM roles required for your ECS task to run successfully. If these roles don't exist in your AWS account, you'll need to create them first.&lt;/p&gt;

&lt;p&gt;Below are the basic definitions for both required roles. Keep in mind that every use case is different, so you may need to extend these definitions with additional permissions based on your specific requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution Role (for task startup)&lt;/strong&gt;&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;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:GetDownloadUrlForLayer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:BatchGetImage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:BatchCheckLayerAvailability"&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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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="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;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ecr:GetAuthorizationToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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="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;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"logs:PutLogEvents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"logs:CreateLogStream"&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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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="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;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;"2012-10-17"&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;Task Role (for runtime permissions)&lt;/strong&gt;&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;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cloudwatch:PutMetricData"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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="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;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;"2012-10-17"&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;Create the roles, and copy/paste ARN’s to your task definition.&lt;/p&gt;

&lt;p&gt;Also don’t forget to create log group for your logs.&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="err"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;logs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;create-log-group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;--log-group-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/ecs/k&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;-load-test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Register the Task Definition
&lt;/h3&gt;

&lt;p&gt;Register your task definition with AWS ECS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecs register-task-definition &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; file://task-definition.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the registration in the AWS ECS console under the &lt;code&gt;k6-ecommerce-load-test&lt;/code&gt; family.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqfstgvqh738o8pfkn7w.webp" 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%2Fyqfstgvqh738o8pfkn7w.webp" alt=" " width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Running Distributed Load Tests
&lt;/h3&gt;

&lt;p&gt;Now the powerful part - running multiple k6 instances simultaneously across your ECS cluster. Here’s the script, fulfill and &lt;strong&gt;launch it&lt;/strong&gt;. I suggest to start with only 1 task, just for safety 😏&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# run-distributed-test.sh &amp;lt;number_of_tasks&amp;gt;&lt;/span&gt;

&lt;span class="nv"&gt;CONCURRENT_TASKS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONCURRENT_TASKS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;number_of_tasks&amp;gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Configuration - Update these values for your environment&lt;/span&gt;
&lt;span class="nv"&gt;CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"k6-load-test-cluster"&lt;/span&gt;
&lt;span class="nv"&gt;TASK_DEFINITION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"k6-ecommerce-load-test"&lt;/span&gt;
&lt;span class="nv"&gt;SUBNET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"subnet-xxxxxxxxx"&lt;/span&gt;
&lt;span class="nv"&gt;SECURITY_GROUP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sg-xxxxxxxxx"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting &lt;/span&gt;&lt;span class="nv"&gt;$CONCURRENT_TASKS&lt;/span&gt;&lt;span class="s2"&gt; k6 load test tasks..."&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONCURRENT_TASKS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting task &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; of &lt;/span&gt;&lt;span class="nv"&gt;$CONCURRENT_TASKS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  aws ecs run-task &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLUSTER_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--task-definition&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TASK_DEFINITION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--launch-type&lt;/span&gt; FARGATE &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--network-configuration&lt;/span&gt; &lt;span class="s2"&gt;"awsvpcConfiguration={subnets=[&lt;/span&gt;&lt;span class="nv"&gt;$SUBNET_ID&lt;/span&gt;&lt;span class="s2"&gt;],securityGroups=[&lt;/span&gt;&lt;span class="nv"&gt;$SECURITY_GROUP&lt;/span&gt;&lt;span class="s2"&gt;],assignPublicIp=ENABLED}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadTest,value&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;TestRun,value&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d-%H%M%S&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All &lt;/span&gt;&lt;span class="nv"&gt;$CONCURRENT_TASKS&lt;/span&gt;&lt;span class="s2"&gt; load test tasks started successfully!"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Monitor progress in AWS ECS console and CloudWatch logs"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Required Configuration Parameters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLUSTER_NAME&lt;/strong&gt;: Your ECS cluster name where tasks will be executed. Create one if needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecs create-cluster &lt;span class="nt"&gt;--cluster-name&lt;/span&gt; k6-load-test-cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TASK_DEFINITION&lt;/strong&gt;: Your registered task definition name (&lt;code&gt;k6-ecommerce-load-test&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUBNET_ID&lt;/strong&gt;: Subnet ID for network configuration (use your default VPC subnet or custom subnet)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SECURITY_GROUP:&lt;/strong&gt; Security group ID (ensure it allows outbound internet access for your tests, you can also use default one from default VPC)&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Optimization Strategies
&lt;/h2&gt;

&lt;p&gt;Running distributed load tests can become expensive without proper optimization. Implement these cost-saving strategies:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Leverage Fargate Spot Capacity
&lt;/h3&gt;

&lt;p&gt;Consider using &lt;strong&gt;Fargate Spot&lt;/strong&gt; for non-critical tests, which can provide up to 70% cost savings.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Right-size Your Resources
&lt;/h3&gt;

&lt;p&gt;Start with minimal CPU/memory allocations and scale up as needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU&lt;/strong&gt;: Begin with 0.5 vCPU and monitor utilization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt;: Start with 1024MB (1GB) and adjust based on test requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor CloudWatch metrics&lt;/strong&gt; to optimize resource allocation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Implement Automatic Cleanup
&lt;/h3&gt;

&lt;p&gt;Ensure tasks terminate after completion to avoid unnecessary charges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices and Monitoring for E-commerce &lt;strong&gt;Load Testing&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Load Testing Best Practices on AWS ECS Fargate&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test in a Staging Environment First&lt;/strong&gt; – Avoid running load tests directly on production to prevent downtime and customer impact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor Database Performance Closely&lt;/strong&gt; – Track queries, indexing, and resource usage to detect bottlenecks early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Geographic Distribution&lt;/strong&gt; – Run distributed load tests from multiple AWS regions for realistic simulation of global shoppers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Real-time Metrics Dashboard with Amazon CloudWatch&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For &lt;strong&gt;distributed load testing with k6 and AWS ECS Fargate&lt;/strong&gt;, set up CloudWatch dashboards to monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ECS task CPU and memory usage&lt;/strong&gt; for scaling insights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test execution progress&lt;/strong&gt; to identify issues early&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target application response times and throughput&lt;/strong&gt; to measure real performance&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Implementing &lt;strong&gt;distributed load testing with k6 and AWS ECS Fargate&lt;/strong&gt; is one of the most effective ways to ensure your &lt;strong&gt;e-commerce website&lt;/strong&gt; can handle peak traffic without compromising user experience. This scalable, cost-efficient approach allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simulate realistic user journeys&lt;/strong&gt; across thousands of concurrent shoppers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize infrastructure costs&lt;/strong&gt; through precise resource management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale testing environments&lt;/strong&gt; to meet seasonal or campaign-specific demand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run tests from multiple geographic regions&lt;/strong&gt; for global performance insight&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize test scripts&lt;/strong&gt; to match your unique business workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combination of k6’s developer-friendly scripting and Fargate’s serverless scalability provides everything you need for &lt;strong&gt;online store performance testing&lt;/strong&gt;—whether you’re preparing for Black Friday, launching a new product, or handling a viral sales spike.&lt;br&gt;
By following this guide to &lt;strong&gt;distributed load testing with k6 and AWS ECS Fargate&lt;/strong&gt;, you’ll be ready to validate and optimize your e-commerce platform’s performance under real-world conditions. Start your first distributed load test today and make sure your &lt;strong&gt;digital storefront&lt;/strong&gt; delivers fast, reliable experiences—even at the busiest times of the year.&lt;/p&gt;

</description>
      <category>k6</category>
      <category>fargate</category>
    </item>
    <item>
      <title>Deploying Next.js on AWS: Why We Migrated from Amplify to ECS?</title>
      <dc:creator>Bartek Gałęzowski</dc:creator>
      <pubDate>Wed, 30 Jul 2025 12:47:45 +0000</pubDate>
      <link>https://forem.com/u11d/deploying-nextjs-on-aws-why-we-migrated-from-amplify-to-ecs-18ic</link>
      <guid>https://forem.com/u11d/deploying-nextjs-on-aws-why-we-migrated-from-amplify-to-ecs-18ic</guid>
      <description>&lt;p&gt;When deploying Next.js applications on AWS, developers face a critical decision between two powerful but fundamentally different platforms: AWS Amplify and AWS ECS (Elastic Container Service). Both offer robust hosting solutions, yet they serve distinct use cases and present unique trade-offs. Understanding these differences is crucial for making an informed architectural decision that will impact your application's performance, scalability, and maintainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Amplify: The Developer-First Platform
&lt;/h2&gt;

&lt;p&gt;AWS Amplify represents the modern approach to application deployment, designed specifically for frontend and full-stack applications. As a fully managed service, Amplify abstracts away the complexities of infrastructure management, allowing developers to focus entirely on building features rather than managing servers. This platform excels particularly well with JAMstack applications and provides an remarkably streamlined deployment experience that can get applications from code to production in minutes.&lt;/p&gt;

&lt;p&gt;The service shines brightest in its commitment to developer productivity, offering several key advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Infrastructure Management&lt;/strong&gt;: Developers never need to configure servers, containers, or load balancers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Scaling&lt;/strong&gt;: Responds to traffic spikes without manual intervention or pre-configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in CI/CD&lt;/strong&gt;: Git-based deployment workflow with preview environments for every branch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global CDN&lt;/strong&gt;: Every deployment automatically leverages CloudFront for worldwide content delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Certificates&lt;/strong&gt;: Automatic HTTPS provisioning and management, even for custom domains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Deployments&lt;/strong&gt;: Zero downtime deployments with instant rollback capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, this simplicity comes with inherent limitations that become apparent as applications grow in complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited Runtime Control&lt;/strong&gt;: Reduced ability to customize the execution environment for specific requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold Start Latency&lt;/strong&gt;: Server-side rendering may experience delays during Lambda function initialization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Time Constraints&lt;/strong&gt;: 15-minute timeout limit can restrict larger applications with complex build pipelines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Isolation&lt;/strong&gt;: Lambda functions cannot share cache between instances, creating performance bottlenecks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Critical Cache Isolation Problem
&lt;/h2&gt;

&lt;p&gt;Perhaps the most significant limitation of AWS Amplify's architecture lies in its inability to share cache between Lambda function instances. This architectural constraint creates a fundamental problem for applications that rely on server-side caching strategies. Each Lambda execution begins with a fresh container, meaning the Next.js build cache stored in the &lt;code&gt;.next/cache&lt;/code&gt; directory cannot persist or be shared between different requests.&lt;/p&gt;

&lt;p&gt;In my latest project, our Next.js application was configured to fetch content from Strapi and cache it for reuse across multiple users. While this seems straightforward, Amplify's Lambda-based architecture made it impossible to implement effectively. Since AWS Lambda functions don't run continuously and are destroyed after periods of inactivity, any cached content is lost when the function terminates. New requests trigger fresh Lambda instances, which must make new requests to Strapi since they cannot access previously cached data.&lt;/p&gt;

&lt;p&gt;The result was a cascading performance problem where our Strapi instance became severely overloaded with redundant requests that should have been served from cache. This not only degraded application performance but also significantly increased our infrastructure costs due to the needs of increasing resources for Strapi machine, excessive API calls and database queries that could have been avoided with proper caching.&lt;/p&gt;

&lt;p&gt;This limitation became the &lt;strong&gt;primary driver for looking for a better solution and migrating from Amplify&lt;/strong&gt;. After weeks of trying various workarounds and caching strategies within Amplify's constraints, it became evident that the solution required a fundamentally different architectural approach. That's when we turned our attention to &lt;strong&gt;AWS ECS&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS ECS: The Container Orchestration Powerhouse
&lt;/h2&gt;

&lt;p&gt;AWS ECS represents a completely different philosophical approach to application deployment. As a fully managed container orchestration service, ECS provides complete control over your application's runtime environment while still handling the underlying infrastructure management. This platform is particularly well-suited for applications requiring custom configurations, microservices architectures, or specific scaling requirements that go beyond what serverless platforms can offer.&lt;/p&gt;

&lt;p&gt;The control that ECS provides is its greatest strength, delivering several significant advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complete Runtime Control&lt;/strong&gt;: Customize every aspect from operating system to specific library dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-grained Scaling&lt;/strong&gt;: Implement custom scaling policies based on CPU, memory, or application-specific metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Integration&lt;/strong&gt;: Seamless access to the entire AWS ecosystem without platform constraints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Performance&lt;/strong&gt;: Long-lived containers eliminate cold starts and provide predictable response times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Networking&lt;/strong&gt;: VPC integration, custom security groups, and sophisticated load balancing strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yet this power comes with increased complexity that teams must carefully consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Knowledge Required&lt;/strong&gt;: Demands container orchestration expertise that may not exist in all teams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Initial Setup&lt;/strong&gt;: Significantly more configuration compared to Amplify's one-click deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ongoing Maintenance&lt;/strong&gt;: Cluster management, task definitions, and capacity planning add operational overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Monitoring&lt;/strong&gt;: Teams must implement their own logging, metrics, and alerting systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moving to AWS ECS represented a fundamental shift in our deployment strategy. Unlike Amplify's serverless approach, ECS runs applications in long-lived containers that can maintain state, share cache between requests, and provide the consistent performance our application demanded. This architectural difference immediately solved our caching problems—containers persist for hours or even days, allowing Next.js to build and maintain its cache across multiple requests. The shared cache that was impossible with Lambda became effortless with ECS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the Strategic Decision
&lt;/h2&gt;

&lt;p&gt;The choice between Amplify and ECS should be driven by your specific application requirements and team capabilities rather than general preferences. Amplify excels when you need rapid prototyping capabilities for MVPs and demo applications where time-to-market is critical. Small teams with limited DevOps expertise will find Amplify's managed approach removes significant operational burden while still providing professional-grade infrastructure.&lt;/p&gt;

&lt;p&gt;Simple applications that utilize standard Next.js features without complex caching requirements or custom runtime needs are ideal candidates for Amplify. The platform's git-based workflow creates an excellent developer experience for teams that want automatic deployments triggered by repository changes. Applications that need global distribution benefit from Amplify's automatic CDN configuration without requiring additional setup or expertise.&lt;/p&gt;

&lt;p&gt;However, you should avoid Amplify if your application relies heavily on server-side caching strategies, as the Lambda-based architecture fundamentally cannot support shared state between requests. Applications with &lt;strong&gt;high-frequency API routes&lt;/strong&gt; that benefit from persistent connections or shared memory will face significant performance constraints. If performance consistency is critical to your user experience, the potential for cold starts in Amplify may be unacceptable. Similarly, applications requiring fine-grained control over caching strategies will find Amplify's managed approach too restrictive.&lt;/p&gt;

&lt;p&gt;ECS becomes the preferred choice when you have custom requirements that demand specific runtime configurations or dependencies not supported by managed platforms. Microservices architectures with multiple services requiring complex communication patterns benefit from ECS's networking flexibility and container orchestration capabilities. Applications demanding high performance with consistent, predictable response times will find ECS's always-on container model superior to serverless alternatives.&lt;/p&gt;

&lt;p&gt;The shared caching capabilities that ECS enables make it particularly beneficial for applications with expensive computations that can be cached and reused across requests. High-traffic applications requiring consistent performance characteristics will appreciate the elimination of cold starts and the ability to maintain persistent connections and in-memory state. Complex applications with multiple interconnected services can leverage ECS's container orchestration to manage sophisticated deployment patterns and service communication.&lt;/p&gt;

&lt;p&gt;Enterprise applications often gravitate toward ECS due to its comprehensive security and compliance capabilities, along with the ability to integrate with existing containerized services in hybrid architectures. Teams with existing container expertise will find ECS's model familiar and powerful, allowing them to leverage their existing knowledge and tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Aligning Technology with Requirements
&lt;/h2&gt;

&lt;p&gt;The beauty of AWS's ecosystem lies in its evolutionary path. Many applications (including mine) begin their journey on Amplify, taking advantage of its rapid deployment capabilities and managed infrastructure to achieve fast time-to-market. As applications grow in complexity and requirements become more sophisticated, migrating to ECS becomes a natural progression rather than a fundamental architecture change.&lt;/p&gt;

&lt;p&gt;Both platforms represent excellent choices within their respective domains, and neither is inherently superior to the other. The key is understanding your current requirements, anticipated growth patterns, and team capabilities. Start with the platform that best matches your immediate needs while keeping an eye on future requirements that might necessitate a different approach.&lt;/p&gt;

&lt;p&gt;Remember that the best architectural decision is one that aligns with your team's expertise, project requirements, and long-term strategic goals. Both Amplify and ECS will serve your Next.js applications exceptionally well when chosen appropriately for your specific context and requirements.&lt;/p&gt;

</description>
      <category>ecs</category>
      <category>amplify</category>
      <category>strapi</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Unleashing the power of serverless: a deep dive into web form deployment with our serverless plugin</title>
      <dc:creator>Bartek Gałęzowski</dc:creator>
      <pubDate>Wed, 25 Jun 2025 06:27:00 +0000</pubDate>
      <link>https://forem.com/u11d/unleashing-the-power-of-serverless-a-deep-dive-into-web-form-deployment-with-our-serverless-plugin-29d8</link>
      <guid>https://forem.com/u11d/unleashing-the-power-of-serverless-a-deep-dive-into-web-form-deployment-with-our-serverless-plugin-29d8</guid>
      <description>&lt;p&gt;Web forms are the lifeblood of online interaction, serving as the primary conduit between users and web services. However, deploying these forms can often feel like navigating a labyrinth. Fear not, as we're here to guide you through this maze.&lt;/p&gt;

&lt;p&gt;In this post, we'll dissect the anatomy of form deployment, comparing the traditional server-side method with the modern serverless approach. We'll also shed light on why serverless may be the best choice, discuss the security measures against bots, and provide a step-by-step guide on how to swiftly and effortlessly create and deploy a form using &lt;a href="https://www.npmjs.com/package/@uninterrupted/serverless-plugin-webform" rel="noopener noreferrer"&gt;@uninterrupted/serverless-plugin-webform&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-side vs. Serverless
&lt;/h2&gt;

&lt;p&gt;Server-side and serverless computing are two different approaches to building and running applications. With server-side computing, the developer is responsible for provisioning and managing servers, while with serverless computing it is handled by the cloud provider. Server-side computing is more traditional, while serverless computing is a newer approach that is gaining popularity.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Old Fellow: Server-side Deployment
&lt;/h3&gt;

&lt;p&gt;Server-side deployment is the old fellow of deploying web forms. In this approach, the server shoulders all the logic related to the form. It's a bit like being a one-man band. It can be a time-consuming and costly endeavor. In the traditional server-side approach, web forms rely on a dedicated server to handle form submissions. This involves setting up and maintaining server infrastructure, managing databases, and handling user requests/form submissions. While this method has been the norm for years, it comes with several challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scalability and Cost:&lt;/strong&gt; Scaling server resources to accommodate varying traffic levels can be complex and costly. Over-provisioning leads to unnecessary expenses, while under-provisioning results in performance issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance:&lt;/strong&gt; Traditional server setups demand continuous maintenance, including security updates, database management, and server monitoring. This can be time-consuming and require technical expertise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Deployment:&lt;/strong&gt; Deploying updates to forms or making changes often involves multiple steps, increasing the chances of errors or downtime.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The New Kid on the Block: Serverless Deployment
&lt;/h3&gt;

&lt;p&gt;Enter serverless deployment, the new kid on the block. This modern approach takes the server management load off your shoulders. It allows you, the developer, to focus on your code, while the infrastructure is handled by the serverless provider. The result? Faster deployment, lower costs, and improved scalability. The serverless approach revolutionizes form deployment by eliminating the need for dedicated servers. It leverages cloud services to handle form submissions dynamically, scaling automatically based on demand. Key benefits include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Auto-scaling:&lt;/strong&gt; Serverless platforms like AWS Lambda or Azure Functions automatically scale resources based on traffic, ensuring optimal performance without manual intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Efficiency:&lt;/strong&gt; With serverless, you pay only for the actual usage, making it more cost-effective as you're not charged for idle resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Deployment:&lt;/strong&gt; Serverless frameworks provide streamlined deployment pipelines, enabling quick updates and reducing the risk of deployment errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Development:&lt;/strong&gt; By abstracting server management, developers can focus on building the form's functionality rather than dealing with infrastructure.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Securing the Digital Neighbourhood
&lt;/h2&gt;

&lt;p&gt;Much like a safe and secure neighborhood, our web forms too require protection from lurking threats, often manifesting as relentless bots. In the realm of web forms, security stands as the guardian defending the estate against thieves use the watchful eye of the honeypot and the housing estate barrier of reCaptcha.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Watchful Eye - Honeypot
&lt;/h3&gt;

&lt;p&gt;Imagine strolling down your friendly neighborhood street, where houses are welcoming and familiar. Among them, a house stands with an unseen, yet effective, guardian – a concealed pitfall that only trespassers unwittingly fall into. Similarly, a web form armed with a honeypot possesses an invisible field, lying in wait for bots that blindly wander through. While legitimate visitors tread without concern, bots stumble into this trap, instantly revealing their true identity. This quiet sentinel may not deter all invaders, but it thwarts those who are less cunning, keeping the virtual streets cleaner and safer.&lt;br&gt;
The honeypot technique is a cybersecurity strategy designed to detect, deflect, or study unauthorized access or malicious activity within a controlled environment. It involves setting up decoy systems, services, or resources that appear to be legitimate targets for attackers but are isolated from the main network infrastructure. The primary purpose of a honeypot is to gather valuable information about the tactics, techniques, and procedures (TTPs) employed by cybercriminals, thereby enhancing an organization's ability to defend against real-world threats.&lt;/p&gt;

&lt;p&gt;Honeypots can be categorized into different types based on their goals and deployment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Production Honeypots:&lt;/strong&gt; These are designed to appear as normal systems or services within the organization's network. The intention is to observe and divert attackers from real assets while gathering information on their activities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Research Honeypots:&lt;/strong&gt; These are more elaborate and specialized systems that focus on in-depth analysis of an attacker's behavior. They often capture detailed information about the attack methods, such as the malware used, command and control traffic, and exploitation techniques.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High-Interaction Honeypots:&lt;/strong&gt; These mimic actual systems and services to a high degree, providing attackers with a realistic environment. While they offer detailed insights, they are also more resource-intensive to maintain and require careful isolation to prevent unauthorized access to the main network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low-Interaction Honeypots:&lt;/strong&gt; These are simpler decoy systems that emulate only a limited set of services, reducing the risk of an attacker gaining full access. They are easier to manage and deploy, but may not provide as much detailed information about attacker behavior.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Benefits of using the honeypot technique include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Early Threat Detection:&lt;/strong&gt; Honeypots can detect attacks that go unnoticed by traditional security measures. By attracting and engaging potential attackers, organizations can identify new attack vectors or vulnerabilities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Behavioral Analysis:&lt;/strong&gt; Detailed logs and data collected from honeypots can be analyzed to understand attackers' methods, tools, and motives. This information helps organizations improve their cybersecurity strategies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced False Positives:&lt;/strong&gt; Since honeypots are isolated from critical infrastructure, any activity detected within them is highly likely to be malicious, reducing false positive alerts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Experiential Learning:&lt;/strong&gt; Cybersecurity professionals can gain hands-on experience with real-world attack scenarios in a controlled environment, enhancing their skills and understanding of threats.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In conclusion, the honeypot technique is a valuable tool in the cybersecurity arsenal, providing organizations with insights into emerging threats and attackers' methods. However, its implementation requires careful planning, management, and consideration of potential risks to ensure its effectiveness in enhancing overall security posture.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Robust Barrier - reCaptcha
&lt;/h3&gt;

&lt;p&gt;Picture your neighborhood equipped with an advanced security gate, challenging those who seek entry. This gate, known as reCaptcha, tasks newcomers with puzzles that distinguish between human interaction and automated trickery. Like a well-trained guard, reCaptcha vouches for genuine residents, while raising an alarm for suspicious characters. Whether it's a simple checkbox or a complex image recognition puzzle, reCaptcha adds an extra layer of protection to our digital community, deterring nefarious bots from entering our online neighborhood.&lt;br&gt;
reCAPTCHA, short for "Completely Automated Public Turing test to tell Computers and Humans Apart," is a widely used security technology developed by Google to distinguish between human users and automated bots on the internet. It was initially introduced as a way to prevent bots from abusing online services, such as submitting forms, creating accounts, or making purchases, while allowing legitimate human users to access these services without unnecessary friction.&lt;/p&gt;

&lt;p&gt;The primary purpose of reCAPTCHA is to provide an additional layer of security to various online platforms by challenging users to prove their human identity through a series of tests that are easy for humans to solve but difficult for automated bots. Over the years, reCAPTCHA has evolved to include several versions, each designed to address different security and user experience needs.&lt;/p&gt;

&lt;p&gt;Here are some key aspects of reCAPTCHA:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CAPTCHA Challenges:&lt;/strong&gt; reCAPTCHA typically presents users with challenges that require cognitive or perceptual skills that bots find difficult to replicate. These challenges have included identifying distorted text, selecting specific images from a grid, or simply checking a box to confirm their human status.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Types of reCAPTCHA:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;reCAPTCHA v2 "I'm not a robot":&lt;/strong&gt; This version introduced the checkbox-style CAPTCHA where users simply need to click on the checkbox to verify their humanity. It often relies on behavioral analysis to determine whether a user is human or not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reCAPTCHA v2 "Invisible reCAPTCHA":&lt;/strong&gt; This version is even less intrusive, as it runs in the background and only displays a CAPTCHA challenge if the system suspects the user might be a bot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reCAPTCHA v3:&lt;/strong&gt; This version focuses on passive user verification. It assigns a risk score to each user interaction, allowing site owners to take action based on the perceived risk level. It doesn't require any explicit user interaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Advanced Technology:&lt;/strong&gt; Google uses a variety of technologies, including machine learning and advanced risk analysis, to continuously improve the accuracy of distinguishing humans from bots. These technologies help reduce the likelihood of false positives (incorrectly identifying humans as bots) and false negatives (failing to identify bots).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration:&lt;/strong&gt; Website owners can integrate reCAPTCHA into their platforms by including Google's reCAPTCHA API in their code. Google provides detailed documentation on how to implement and customize reCAPTCHA to suit the website's needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Experience Considerations:&lt;/strong&gt; While reCAPTCHA serves as a security measure, it's important to consider user experience. Overly complex challenges can frustrate users and discourage them from using a website. Balancing security with a user-friendly interface is crucial.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Accessibility:&lt;/strong&gt; Google has taken steps to make reCAPTCHA more accessible to users with disabilities. For example, audio challenges are provided for visually impaired users who may have difficulty with visual challenges.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In recent years, the evolution of reCAPTCHA has led to debates about its effectiveness, user privacy concerns, and the potential for unintended consequences. Some users find certain challenges, especially those involving image recognition, to be frustrating or time-consuming. Additionally, there are concerns about the amount of behavioral data that Google collects as users interact with reCAPTCHA challenges.&lt;/p&gt;

&lt;p&gt;In conclusion, reCAPTCHA plays a significant role in safeguarding online platforms from automated bots and enhancing overall cybersecurity. However, its implementation should be balanced with user experience considerations and privacy concerns to ensure a seamless and secure online environment for both humans and legitimate users.&lt;/p&gt;
&lt;h3&gt;
  
  
  An Impenetrable Alliance
&lt;/h3&gt;

&lt;p&gt;Much like a vigilant neighborhood combining security systems, the strategic union of honeypot and reCaptcha forms an impervious barrier against the dragon of bots. While the honeypot lures and traps the naive intruders, reCaptcha stands tall as the fortified castle gate, testing the mettle of even the craftiest foes. By employing both methods, we build a formidable defense, ensuring that our virtual streets remain safe for residents while deterring those who aim to exploit our online community.&lt;/p&gt;

&lt;p&gt;In the ever-evolving landscape of the web, where bots prowl like shadowy figures, the harmonious coexistence of honeypot and reCaptcha serves as our armor. Just as a tight-knit neighborhood watches out for its own, these security measures safeguard our digital spaces, enabling seamless interactions for genuine users while safeguarding against the encroaching darkness of automated threats.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Neighborhood Builder: Streamlined Form Deployment with @uninterrupted/serverless-plugin-webform
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@uninterrupted/serverless-plugin-webform" rel="noopener noreferrer"&gt;@uninterrupted/serverless-plugin-webform&lt;/a&gt; plugin is your guide to easy and secure form deployment. Here's how it works:&lt;/p&gt;

&lt;p&gt;Begin with installing the plugin. Run the following command in your terminal within your serverless project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="nx"&gt;uninterrupted&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;tech&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;webform&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use other package managers such as yarn or pnpm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;uninterrupted&lt;/span&gt;&lt;span class="sr"&gt;/serverless-plugin-webfor&lt;/span&gt;&lt;span class="err"&gt;m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;pnpm&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;uninterrupted&lt;/span&gt;&lt;span class="sr"&gt;/serverless-plugin-webfor&lt;/span&gt;&lt;span class="err"&gt;m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that you have to add the plugin to your serverless.yaml file. To do this add the name of the plugin to the plugins array:&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;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="s"&gt;uninterrupted/serverless-plugin-webform&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure the plugin according to your preferences. Add or extend the &lt;code&gt;custom&lt;/code&gt; property in your serverless.yaml file by &lt;code&gt;pluginWebform&lt;/code&gt;. Here we will place all configuration of the plugin.&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;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pluginWebform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin has been designed with the intention of automating the form deployment process as much as possible. However, there is one property that requires user input: the configuration for &lt;a href="https://aws.amazon.com/ses/" rel="noopener noreferrer"&gt;Amazon Simple Email Service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Amazon Simple Email Service is responsible for sending email notifications when someone fill the form and for sending email confirmation to the visitor. Since the email notification feature is optional, the visitor email confirmation is not, and you have to fill in the configuration of the email address from which the emails will be sent.&lt;/p&gt;

&lt;p&gt;To do this, add a new property to your configuration named &lt;code&gt;ses&lt;/code&gt; which stands for Simple Email Service, and set the &lt;code&gt;sourceAddress&lt;/code&gt; property.&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;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pluginWebform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;sourceAddress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hello@uninterrupted.tech&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is actually all that is required. You can deploy the service with the default configuration.&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="s"&gt;serverless deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, you will receive an endpoint to Amazon API Gateway that will execute the lambda function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detailed configuration
&lt;/h3&gt;

&lt;p&gt;However, as the creators, we want to make it as flexible and configurable as possible. So, let's dive a bit deeper. In the next section we will guide you through each part of plugin configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple Email Service (SES)
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, the &lt;code&gt;ses&lt;/code&gt; section contains the configuration for Amazon Simple Email Service&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;ses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sourceAddress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;source@address.com&lt;/span&gt;
  &lt;span class="na"&gt;notificationAddresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;notification@address.com&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;notification@address2.com&lt;/span&gt;
  &lt;span class="na"&gt;visitorNotification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;message"&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./templates/visitor-notification.txt&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./templates/visitor-notification.html&lt;/span&gt;
  &lt;span class="na"&gt;visitorConfirmation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hello {{ name }}!&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./templates/visitor-confirmation.txt&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./templates/visitor-confirmation.html&lt;/span&gt;
  &lt;span class="na"&gt;visitorConfirmationWitMessage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hello {{ name }}!&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./templates/visitor-confirmation-with-message.txt&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./templates/visitor-confirmation-with-message.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sourceAddress&lt;/code&gt; - email address from which the emails will be sent,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;notificationAddresses&lt;/code&gt; - You can add as many notification addresses as you need. When the array will contain at least one element, the lambda function will send a notification about new visitor to the email address. We intended to provide a functionality to notify admins, form owners, and maintainers about new visitors. If you do not configure the visitor notification property, it will be disabled.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;visitorNotification&lt;/code&gt; - the optional notification email template configuration property. If no value is provided, the lambda will use the default template. Otherwise, you must fill three fields to provide your own template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;subject&lt;/code&gt; - contains a subject of the email. You can use replacement tags here, such as &lt;code&gt;{{ name }}&lt;/code&gt;. The replacement tags are used to personalize the email by inserting dynamic content, such as the recipient's name, into the subject line. The plugin &lt;a href="https://github.com/uninterrupted-tech/serverless-plugin-webform#replacement-tags" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; will provide a list of all the available replacement tags.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;html&lt;/code&gt; - contains path from root directory to your .html template message. You can also use replacement tags here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text&lt;/code&gt; - contains path from root directory to your .txt template message. You can also use replacement tags here.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The HTML file is used to define the visual appearance of the email. However, some email clients may not display HTML emails correctly. To ensure that all recipients can read your email, you should also define a plain text version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;visitorConfirmation&lt;/code&gt; - The template is optional and can be used to send a confirmation message to the visitor. The configuration is the same for both the HTML and text templates.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;visitorConfirmationWithMessage&lt;/code&gt; - The template can be used to send a confirmation message with the message/description provided in the form. The lambda function will use this template if the &lt;code&gt;ccMe&lt;/code&gt; property in the API request is set to true. The template is optional.&lt;/p&gt;

&lt;h3&gt;
  
  
  Properties/Honeypot technique
&lt;/h3&gt;

&lt;p&gt;As explained, the honeypot technique is a way to protect your forms from spam bots. Bots will usually fill out all of the fields searching for a common patterns. This way, you can identify spam bots and block them from submitting your form.&lt;/p&gt;

&lt;p&gt;To use the honeypot technique, you need to configure the &lt;code&gt;properties&lt;/code&gt; property in a configuration. This property is a mapping between a property and its value. The &lt;code&gt;name&lt;/code&gt; property is an alias of the real field.&lt;br&gt;
As an example instead of &lt;code&gt;email: hello@uninterrupted.tech&lt;/code&gt; we will send &lt;code&gt;ssadads: hello@uninterrupted.tech&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;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;email&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;ssadads&lt;/span&gt;
  &lt;span class="na"&gt;name&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;wmhhgio&lt;/span&gt;
  &lt;span class="na"&gt;phoneNumber&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;idocynv&lt;/span&gt;
  &lt;span class="na"&gt;description&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;xhqpdaf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration allows to configure four properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;email,&lt;/li&gt;
&lt;li&gt;name,&lt;/li&gt;
&lt;li&gt;phoneNumber, and&lt;/li&gt;
&lt;li&gt;description.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;properties&lt;/code&gt; section is optional. If you do not provide any configuration, the honeypot technique will not be used.&lt;/p&gt;

&lt;h3&gt;
  
  
  reCAPTCHA
&lt;/h3&gt;

&lt;p&gt;reCAPTCHA is a more advanced security mechanism that helps identify and block abusive traffic on a website. reCAPTCHA v3 does this without interrupting users with CAPTCHA challenges. Instead, reCAPTCHA v3 returns a score that you can use to decide how to handle each request. The reCAPTCHA configuration is quite simple and requires configuration of &lt;code&gt;captcha&lt;/code&gt; property.&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;captcha&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret&lt;/span&gt;
  &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;secret&lt;/code&gt; - reCAPTCHA secret value,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;threshold&lt;/code&gt; - the score threshold. Requests with a score less than the threshold will be blocked and reported.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;captcha&lt;/code&gt; section is optional. If you do not fill it out, the CAPTCHA will be turned off. If you provide the &lt;code&gt;secret&lt;/code&gt; property, the CAPTCHA will be enabled. However, it is important to &lt;strong&gt;never load the secret using an environment variable or put sensitive data directly in your code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The default value for the threshold is 0.5. You can experiment with different values to find the best threshold for your needs. Here you can learn more about the &lt;a href="https://developers.google.com/recaptcha/docs/v3#interpreting_the_score" rel="noopener noreferrer"&gt;score threshold&lt;/a&gt; and learn how to &lt;a href="https://developers.google.com/recaptcha/intro" rel="noopener noreferrer"&gt;configure reCAPTCHA and generate secret&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lambda function
&lt;/h3&gt;

&lt;p&gt;This section covers the technical aspects of the configuration, specifically the Lambda function settings. While it is optional, you can modify the Lambda function name if you need to increase its resources or if the default name is already in use. The configuration is under the &lt;code&gt;lambda&lt;/code&gt; property.&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;lambda&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;myLambda&lt;/span&gt;
  &lt;span class="na"&gt;memorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; - the Lambda function name. Combined with the service and deployment stage name to form a unique identifier. The format is:
&lt;code&gt;${serviceName}${stageName}${lambdaName}&lt;/code&gt;.
The default value is &lt;code&gt;createVisitor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;memorySize&lt;/code&gt; - specifies the amount of memory and computing power allocated to the lambda function, measured in megabytes. The default value is 1024MB.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DynamoDB
&lt;/h3&gt;

&lt;p&gt;DynamoDB is a fully managed, serverless, key-value NoSQL database. Here, we use it to store information about visitors and bots (e.g., data provided and reCAPTCHA details). Since two tables are required (one for visitors and one for bots), we have added the possibility to configure the names of the databases to prevent naming conflicts. The table name is a concatenation of the service and stage names, following the format:&lt;br&gt;
&lt;code&gt;${serviceName}${stageName}${tableName}&lt;/code&gt;, similarly to the Lambda naming convention applied previously. This configuration is located under the &lt;code&gt;dynamoDb&lt;/code&gt; property and is entirely optional. When not provided, tables will be named using default values: &lt;code&gt;visitors&lt;/code&gt; and &lt;code&gt;bot-visitors&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;dynamoDb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;visitorsTableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;contacts&lt;/span&gt;
  &lt;span class="na"&gt;botVisitorsTableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bot-contacts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;visitorsTableName&lt;/code&gt; - property specifies the name of the table where comprehensive visitor information will be stored. The default value is &lt;code&gt;visitors&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;botVisitorsTableName&lt;/code&gt; - property specifies the name of the table dedicated to storing data regarding bot visitors. The default value is &lt;code&gt;bot-visitors&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Slack integration
&lt;/h3&gt;

&lt;p&gt;Slack is a popular communication tool used by many organizations. We use it ourselves, and that's why we decided to add support for Slack notifications to our plugin. You can configure Slack notifications to be sent for new visitors. This is an optional feature, but the setup process is straightforward. The configuration for Slack is under the &lt;code&gt;slack&lt;/code&gt; property.&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;slack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX&lt;/span&gt;
  &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#web-form-notifications"&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webhook-bot&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./slack/message.txt&lt;/span&gt;
  &lt;span class="na"&gt;iconEmoji&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:email:"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt; - Slack incoming webhook URL. Please refer to the &lt;a href="https://api.slack.com/messaging/webhooks" rel="noopener noreferrer"&gt;instruction&lt;/a&gt; on how to generate the Slack incoming webhook URL,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;channel&lt;/code&gt; - name of an existing channel in your Slack organization where notifications will be sent,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt; - the username of the message sender,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;message&lt;/code&gt; - path from the root directory to your .txt template message. Within this file, you can define the appearance of your messages. Similar to the SES templates you can also use replacement tags here,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iconEmoji&lt;/code&gt; - refers to the icon emoji of the channel. You can choose from a variety of emojis listed &lt;a href="https://www.webfx.com/tools/emoji-cheat-sheet/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. By default emoji is "📧" &lt;code&gt;:email:&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In the ever-evolving landscape of web development, the deployment of web forms has transitioned from the traditional server-side approach to the modern serverless paradigm. This shift has brought about enhanced scalability, reduced costs, simplified deployment processes, and faster development cycles. To fortify the security of our digital neighborhoods against the onslaught of automated threats, the strategic alliance of honeypot and reCaptcha techniques provides a robust defense. In conclusion, the synergy between serverless deployment, honeypot and reCaptcha security techniques, and the &lt;a href="https://www.npmjs.com/package/@uninterrupted/serverless-plugin-webform" rel="noopener noreferrer"&gt;@uninterrupted/serverless-plugin-webform&lt;/a&gt; plugin transforms the deployment and protection of web forms into a seamless and efficient process. By leveraging these tools, developers can enhance user experiences, bolster security measures, and contribute to the creation of a safer and more user-friendly digital environment.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Hassle free Redis Cluster deployment using Docker</title>
      <dc:creator>Bartek Gałęzowski</dc:creator>
      <pubDate>Wed, 25 Jun 2025 06:24:46 +0000</pubDate>
      <link>https://forem.com/u11d/hassle-free-redis-cluster-deployment-using-docker-12ag</link>
      <guid>https://forem.com/u11d/hassle-free-redis-cluster-deployment-using-docker-12ag</guid>
      <description>&lt;p&gt;Distributed Redis Cluster is an efficient solution for applications that require a scalable and reliable data store. Redis Cluster enables automatic data partitioning and replication within the cluster, ensuring high availability and fault tolerance. In this article, we will discuss how to run Redis Cluster using Docker. &lt;/p&gt;

&lt;p&gt;We will start by preparing and describing in detail the Docker Compose file with Redis Cluster. To accomplish this, we will utilize the &lt;code&gt;bitnami/redis-cluster&lt;/code&gt; image, which will enable us to run six containers (Redis Nodes), with one of them being designated as responsible for creating the cluster. &lt;/p&gt;

&lt;p&gt;The cluster consists of nodes categorized as masters and slaves. Each master node can have one or multiple assigned slaves, which essentially act as replicas of the master. In the event of a master node failure, its corresponding slave node automatically takes over and becomes the new master. This mechanism ensures high availability within the cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composing a multi-container Redis Cluster
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;x-redis-base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;redis-base&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;docker.io/bitnami/redis-cluster:7.0.10&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;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
    &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The anchor contains the fundamental configuration for the Redis node. The presence of &lt;code&gt;ALLOW_EMPTY_PASSWORD&lt;/code&gt; permits connecting to the cluster without requiring credentials, which aligns with our requirements.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that this article shows setup for development purposes, but we encourage to use authentication everywhere. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, &lt;code&gt;REDIS_NODES&lt;/code&gt; comprises the hostnames of all nodes that will constitute the cluster, separated by spaces.&lt;br&gt;
Once we have created the anchor with the basic configuration, we can utilize it to generate five individual nodes.&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;redis-node-0&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-0&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
&lt;span class="na"&gt;redis-node-1&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-1&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
&lt;span class="na"&gt;redis-node-2&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-2&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
&lt;span class="na"&gt;redis-node-3&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-3&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
&lt;span class="na"&gt;redis-node-4&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-4&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last, sixth node (&lt;code&gt;redis-node-5&lt;/code&gt;), will have the specific role of establishing the cluster by setting the &lt;code&gt;REDIS_CLUSTER_CREATOR&lt;/code&gt; parameter.&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;redis-node-5&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-5&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
  &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-0&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-1&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-3&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-4&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;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
    &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5&lt;/span&gt;
    &lt;span class="na"&gt;REDIS_CLUSTER_REPLICAS&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;REDIS_CLUSTER_CREATOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set &lt;code&gt;REDIS_CLUSTER_REPLICAS: 1&lt;/code&gt; what based on six nodes give us three masters and three slaves.&lt;/p&gt;

&lt;p&gt;Now our docker-compose file looks like below:&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;x-redis-base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;redis-base&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;docker.io/bitnami/redis-cluster:7.0.10&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;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
    &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-0&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-0&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-1&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-1&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-2&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-2&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-3&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-3&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-4&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-4&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;

  &lt;span class="na"&gt;redis-node-5&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-5&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-4&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;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_REPLICAS&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;REDIS_CLUSTER_CREATOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's execute &lt;code&gt;docker-compose up&lt;/code&gt; to run the Redis Cluster.&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="gp"&gt;redis-node-5  | &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Performing Cluster Check &lt;span class="o"&gt;(&lt;/span&gt;using node 172.18.0.2:6379&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;redis-node-5  | M: 025420b382c7d631ebfe4959b346eaf255caeaba 172.18.0.2:6379
redis-node-5  |    slots:[0-5460] (5461 slots) master
redis-node-5  |    1 additional replica(s)
redis-node-5  | S: 1d693ab3d6b630768359615889bfcdc69642cea3 172.18.0.5:6379
redis-node-5  |    slots: (0 slots) slave
redis-node-5  |    replicates 025420b382c7d631ebfe4959b346eaf255caeaba
redis-node-5  | M: 9239ec8481a3ec411dd0f2f832d0f7a4b07472ef 172.18.0.6:6379
redis-node-5  |    slots:[10923-16383] (5461 slots) master
redis-node-5  |    1 additional replica(s)
redis-node-5  | S: 4d8b42ab42c2f589f31ea2310ccc630a899d71f1 172.18.0.7:6379
redis-node-5  |    slots: (0 slots) slave
redis-node-5  |    replicates 9c5e768c2aaae24e48bb62c0be8395d13592a940
redis-node-5  | S: 07fc9c307fc4fca6371e8832821638d412c8c350 172.18.0.4:6379
redis-node-5  |    slots: (0 slots) slave
redis-node-5  |    replicates 9239ec8481a3ec411dd0f2f832d0f7a4b07472ef
redis-node-5  | M: 9c5e768c2aaae24e48bb62c0be8395d13592a940 172.18.0.3:6379
redis-node-5  |    slots:[5461-10922] (5462 slots) master
redis-node-5  |    1 additional replica(s)
redis-node-5  | [OK] All nodes agree about slots configuration.
&lt;/span&gt;&lt;span class="gp"&gt;redis-node-5  | &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Check &lt;span class="k"&gt;for &lt;/span&gt;open slots...
&lt;span class="gp"&gt;redis-node-5  | &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Check slots coverage...
&lt;span class="go"&gt;redis-node-5  | [OK] All 16384 slots covered.
redis-node-5  | Cluster correctly created
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Created!&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the Redis Cluster
&lt;/h2&gt;

&lt;p&gt;Next, let's consider a hypothetical scenario where we have an application that needs to connect to our Redis Cluster. This application is also executed within a Docker container so shares the same network. To simulate the application connection we will connect to our cluster using &lt;code&gt;redis-cli&lt;/code&gt; from within one of the created containers.&lt;/p&gt;

&lt;p&gt;Let’s run &lt;code&gt;docker ps&lt;/code&gt; to list all containers.&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;❯ docker ps
CONTAINER ID   IMAGE                          COMMAND                  CREATED              STATUS              PORTS      NAMES
4a0e6c6ce02a   bitnami/redis-cluster:7.0.10   "/opt/bitnami/script…"   About a minute ago   Up About a minute   6379/tcp   redis-node-5
ff2570e6567c   bitnami/redis-cluster:7.0.10   "/opt/bitnami/script…"   About a minute ago   Up About a minute   6379/tcp   redis-node-2
18b9f0a68e87   bitnami/redis-cluster:7.0.10   "/opt/bitnami/script…"   About a minute ago   Up About a minute   6379/tcp   redis-node-1
2375812e5c93   bitnami/redis-cluster:7.0.10   "/opt/bitnami/script…"   About a minute ago   Up About a minute   6379/tcp   redis-node-0
492f879f178c   bitnami/redis-cluster:7.0.10   "/opt/bitnami/script…"   About a minute ago   Up About a minute   6379/tcp   redis-node-3
77e0549a66a8   bitnami/redis-cluster:7.0.10   "/opt/bitnami/script…"   About a minute ago   Up About a minute   6379/tcp   redis-node-4

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

&lt;/div&gt;



&lt;p&gt;And now use &lt;code&gt;docker exec&lt;/code&gt; to go into the selected container:&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;❯ docker exec -it 4a0e6c6ce02a sh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, now we are in and we can run &lt;code&gt;redis-cli&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;redis-cli &lt;span class="nt"&gt;-c&lt;/span&gt;
&lt;span class="gp"&gt;127.0.0.1:6379&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks that everything is functioning properly. However, to verify whether our cluster is correctly configured, let's execute the &lt;code&gt;CLUSTER NODES&lt;/code&gt; command to retrieve a list of all nodes within the cluster.&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="gp"&gt;127.0.0.1:6379&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CLUSTER NODES
&lt;span class="go"&gt;82d2bee961c7a4b73f099550d72a232e55d12d60 172.18.0.2:6379@16379 master - 0 1683707451345 2 connected 5461-10922
e09bd0f03f71d5ca9e1db6804ac28f2470eb70db 172.18.0.7:6379@16379 myself,slave 82d2bee961c7a4b73f099550d72a232e55d12d60 0 1683707449000 2 connected
8cb609962e074807b56f1a01bbbde3e6a436e7ca 172.18.0.3:6379@16379 master - 0 1683707450331 1 connected 0-5460
7dd24d5ffc732189f7b32a5468d9b20ebc1f60d7 172.18.0.5:6379@16379 master - 0 1683707450000 3 connected 10923-16383
c02f2c7285074d9d5b23b8faf07ec287c6e94636 172.18.0.4:6379@16379 slave 8cb609962e074807b56f1a01bbbde3e6a436e7ca 0 1683707449316 1 connected
5105b2780cabbff516e0f834a694bcdca310e8b7 172.18.0.6:6379@16379 slave 7dd24d5ffc732189f7b32a5468d9b20ebc1f60d7 0 1683707448291 3 connected
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Based on the provided information, we have a Redis Cluster consisting of three masters and three slaves, with each master having its corresponding slave. The "myself" indicator specifies the node to which we are currently connected.&lt;/p&gt;

&lt;p&gt;Now, let's proceed to set a value in the cluster.&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="gp"&gt;127.0.0.1:6379&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SET key &lt;span class="s2"&gt;"value"&lt;/span&gt;
&lt;span class="gp"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Redirected to slot &lt;span class="o"&gt;[&lt;/span&gt;12539] located at 172.18.0.5:6379
&lt;span class="go"&gt;OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voilà! Thanks to the &lt;strong&gt;&lt;code&gt;-c&lt;/code&gt;&lt;/strong&gt; flag, which runs redis-cli in cluster mode, we have been redirected to the master node responsible for slot 12539, allowing us to write our value.&lt;/p&gt;

&lt;p&gt;No problemo, but…&lt;/p&gt;

&lt;h2&gt;
  
  
  Outside Docker Network
&lt;/h2&gt;

&lt;p&gt;Imagine that your hypothetical application is not ready yet, and you would like to expose Redis Cluster outside of the Docker network. This will give you the possibility to develop application without the need to run it in Docker with every change you made. To achieve this, we will make some modifications to our cluster creator container (&lt;code&gt;redis-node-5&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;redis-node-5&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-5&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
  &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-0&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-1&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-3&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-4&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="s"&gt;6379:6379&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;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
    &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5&lt;/span&gt;
    &lt;span class="na"&gt;REDIS_CLUSTER_REPLICAS&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;REDIS_CLUSTER_CREATOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve added the "ports" keyword, which maps the local port &lt;code&gt;6379&lt;/code&gt; to port &lt;code&gt;6379&lt;/code&gt; inside the Docker network. As a result, we should now be able to connect to the Redis Cluster from the local network (local terminal).&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;❯ redis-cli -c
&lt;/span&gt;&lt;span class="gp"&gt;127.0.0.1:6379&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CLUSTER NODES
&lt;span class="go"&gt;4359876c953a0d1577b75203cddde191b2ac69bc 172.20.0.4:6379@16379 slave 5c7b8bd362ff414ac41861ea8cf5758a1d981bcc 0 1683709816000 3 connected
a95eb7d5b68e925bc8adff2ee79cf2384889a5c2 172.20.0.6:6379@16379 master - 0 1683709816687 2 connected 5461-10922
5c7b8bd362ff414ac41861ea8cf5758a1d981bcc 172.20.0.3:6379@16379 master - 0 1683709814651 3 connected 10923-16383
440ec475bf64e1496ba661b3f7ac49a77f2379cc 172.20.0.5:6379@16379 slave ce88b4c234d6e7203ef94c5970cd85ca38c4bce0 0 1683709816000 1 connected
de7b60d66e94a2905afd5d450bd2029690564f74 172.20.0.7:6379@16379 myself,slave a95eb7d5b68e925bc8adff2ee79cf2384889a5c2 0 1683709815000 2 connected
ce88b4c234d6e7203ef94c5970cd85ca38c4bce0 172.20.0.2:6379@16379 master - 0 1683709815665 1 connected 0-5460
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And setting a value…&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="gp"&gt;127.0.0.1:6379&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SET key &lt;span class="s2"&gt;"value"&lt;/span&gt;
&lt;span class="gp"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Redirected to slot &lt;span class="o"&gt;[&lt;/span&gt;12539] located at 172.20.0.3:6379
&lt;span class="go"&gt;Could not connect to Redis at 172.20.0.3:6379: Operation timed out
(75.00s)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oops... Redis is unable to connect to the master behind &lt;code&gt;172.22.0.3:6379&lt;/code&gt;. However, if we think about it, the reason becomes apparent. As mentioned earlier, we were attempting to set a key, but we can not do that because we were connected to a slave node (indicated by the "myself" tag). When we asked Redis about the location of "our master," Redis responded with "Okay, you have to connect to &lt;code&gt;172.20.0.3:6379&lt;/code&gt;". However, since we didn't expose every node outside of the Docker network, we received an "Operation timed out" error, we don’t see other nodes. And here begins our problem... &lt;/p&gt;

&lt;h3&gt;
  
  
  Oh, wait! Are you Linux user? You’re lucky man!
&lt;/h3&gt;

&lt;p&gt;In Linux we are able to use network host mode, in this mode the container shares the network stack of the host, meaning it uses the host's network interfaces directly, so allows us to use &lt;a href="http://localhost" rel="noopener noreferrer"&gt;localhost&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All we have to do is to add &lt;code&gt;network_mode: host&lt;/code&gt; for each node (I’ve added it in anchor), add &lt;code&gt;REDIS_PORT_NUMBER&lt;/code&gt; environment for each node and modify &lt;code&gt;REDIS_NODES&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.7"&lt;/span&gt;

&lt;span class="na"&gt;x-redis-base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;redis-base&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;docker.io/bitnami/redis-cluster:7.0.10&lt;/span&gt;
  &lt;span class="na"&gt;network_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-0&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-0&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6370&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-1&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-1&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6371&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-2&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-2&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;6372&lt;/span&gt; 
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-3&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-3&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6373&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-4&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-4&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6374&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-5&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-5&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-node-4&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;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6375&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_REPLICAS&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;REDIS_CLUSTER_CREATOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;docker compose up&lt;/code&gt; and testing&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="gp"&gt;~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;redis-cli &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;-p&lt;/span&gt; 6375
&lt;span class="gp"&gt;127.0.0.1:6375&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CLUSTER NODES
&lt;span class="go"&gt;88d08d5a84f7f9879623111c4b3bae028e465d0a 127.0.1.1:6373@16373 slave a816a5d3484177bb90c86e59d1a93a1e81d4bb2d 0 1683801476781 3 connected
69ab6dbf70298ffe19f388440703f30de177e59f 127.0.1.1:6370@16370 master - 0 1683801475761 1 connected 0-5460
b1ab22f03c0c99f17dce4980944d7180f94c154c 127.0.1.1:6371@16371 master - 0 1683801474743 2 connected 5461-10922
a816a5d3484177bb90c86e59d1a93a1e81d4bb2d 127.0.1.1:6372@16372 master - 0 1683801476000 3 connected 10923-16383
9a79480966b1da9a7f10e5c7304b0c3463e93176 127.0.1.1:6375@16375 myself,slave b1ab22f03c0c99f17dce4980944d7180f94c154c 0 1683801475000 2 connected
ebf22feb960eeaf1a7642deec1910a182e900c6f 127.0.1.1:6374@16374 slave 69ab6dbf70298ffe19f388440703f30de177e59f 0 1683801476000 1 connected
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see we have all nodes run on the same host, but on different ports. Let’s try set our test value.&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="gp"&gt;127.0.0.1:6375&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SET key &lt;span class="s2"&gt;"value"&lt;/span&gt;
&lt;span class="gp"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Redirected to slot &lt;span class="o"&gt;[&lt;/span&gt;12539] located at 127.0.1.1:6372
&lt;span class="go"&gt;OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay! We’ve redirected from the node running on 127.0.0.1:6375 to 127.0.0.1:6372, and were able to write the value without any issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are you macOS user? Solution is more complex
&lt;/h3&gt;

&lt;p&gt;Unlike Linux, other operating systems, such as macOS, spins-up Linux VM under the hood which makes real host networking unavailable. To resolve this problem, we need to expose each node outside the Docker network on the same host but on different ports. However, determining the exact host to set becomes challenging. We cannot use &lt;code&gt;127.0.0.1&lt;/code&gt;. Instead, we can obtain the external host IP and utilize it in both &lt;code&gt;REDIS_NODES&lt;/code&gt; and &lt;code&gt;REDIS_CLUSTER_ANNOUCE_IP&lt;/code&gt; configurations to ensure proper communication.&lt;/p&gt;

&lt;p&gt;To receive your current external host IP you can use:&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;route get uninterrupted.tech | &lt;span class="nb"&gt;grep &lt;/span&gt;interface | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/.*: //'&lt;/span&gt; | xargs ipconfig getifaddr&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My IP is &lt;code&gt;10.0.18.181&lt;/code&gt; so Docker Compose will looks like below:&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.7"&lt;/span&gt;

&lt;span class="na"&gt;x-redis-base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;redis-base&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;docker.io/bitnami/redis-cluster:7.0.10&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-0&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-0&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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="s"&gt;6370:6370&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;16370:16370&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;REDIS_CLUSTER_DYNAMIC_IPS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_ANNOUNCE_IP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6370&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181:6370 10.0.18.181:6371 10.0.18.181:6372 10.0.18.181:6373 10.0.18.181:6374 10.0.18.181:6375&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-1&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-1&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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="s"&gt;6371:6371&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;16371:16371&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;REDIS_CLUSTER_DYNAMIC_IPS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_ANNOUNCE_IP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6371&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181:6370 10.0.18.181:6371 10.0.18.181:6372 10.0.18.181:6373 10.0.18.181:6374 10.0.18.181:6375&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-2&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-2&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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="s"&gt;6372:6372&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;16372:16372&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;REDIS_CLUSTER_DYNAMIC_IPS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_ANNOUNCE_IP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6372&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181:6370 10.0.18.181:6371 10.0.18.181:6372 10.0.18.181:6373 10.0.18.181:6374 10.0.18.181:6375&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-3&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-3&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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="s"&gt;6373:6373&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;16373:16373&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;REDIS_CLUSTER_DYNAMIC_IPS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_ANNOUNCE_IP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6373&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181:6370 10.0.18.181:6371 10.0.18.181:6372 10.0.18.181:6373 10.0.18.181:6374 10.0.18.181:6375&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-4&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-4&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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="s"&gt;6374:6374&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;16374:16374&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;REDIS_CLUSTER_DYNAMIC_IPS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_ANNOUNCE_IP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6374&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181:6370 10.0.18.181:6371 10.0.18.181:6372 10.0.18.181:6373 10.0.18.181:6374 10.0.18.181:6375&lt;/span&gt;
  &lt;span class="na"&gt;redis-node-5&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis-node-5&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*redis-base&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="s"&gt;6375:6375&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;16375:16375&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;REDIS_CLUSTER_DYNAMIC_IPS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_ANNOUNCE_IP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_PORT_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6375&lt;/span&gt;
      &lt;span class="na"&gt;ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_NODES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.18.181:6370 10.0.18.181:6371 10.0.18.181:6372 10.0.18.181:6373 10.0.18.181:6374 10.0.18.181:6375&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_CLUSTER_REPLICAS&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;REDIS_CLUSTER_CREATOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After modified the &lt;code&gt;REDIS_NODES&lt;/code&gt; configuration and added &lt;code&gt;REDIS_CLUSTER_ANNOUCE_IP&lt;/code&gt;, we needed to set &lt;code&gt;REDIS_CLUSTER_DYNAMIC_IPS&lt;/code&gt; to "no". Additionally, we extended the ports array to include new ports known as &lt;code&gt;CLUSTER BUS PORTS&lt;/code&gt;. These ports are utilized for various purposes such as failure detection, configuration updates, failover authorization, and more. By default, the cluster bus port is calculated by adding 10000 to the data port (e.g. 16375).&lt;/p&gt;

&lt;p&gt;Let’t connect to Redis Cluster and check cluster nodes:&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;❯ redis-cli -c -h 127.0.0.1 -p 6375
&lt;/span&gt;&lt;span class="gp"&gt;127.0.0.1:6375&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CLUSTER NODES
&lt;span class="go"&gt;275c337ffe6073071088bb0706a2536608c03fe9 192.168.1.184:6372@16372 master - 0 1683717016000 3 connected 10923-16383
0e779e30ce3879ee37b470b299a97d41c0e16779 192.168.1.184:6370@16370 master - 0 1683717018000 1 connected 0-5460
3e7dc06681938425f71a39a2a7d189b336132e4a 192.168.1.184:6374@16374 slave 0e779e30ce3879ee37b470b299a97d41c0e16779 0 1683717020706 1 connected
841b05c2890454b345da8380ee63fa41c03fae6b 192.168.1.184:6371@16371 master - 0 1683717020000 2 connected 5461-10922
44444520eb2bbc346ab4dba9b1c495973399a227 192.168.1.184:6375@16375 myself,slave 841b05c2890454b345da8380ee63fa41c03fae6b 0 1683717017000 2 connected
8f14d7d1faac0e131d315ee34c0c7fc3836716c9 192.168.1.184:6373@16373 slave 275c337ffe6073071088bb0706a2536608c03fe9 0 1683717019000 3 connected
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Each node has the same IP, different port and different bus port. &lt;br&gt;
Final set key attempt…&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="gp"&gt;127.0.0.1:6375&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SET key &lt;span class="s2"&gt;"value"&lt;/span&gt;
&lt;span class="gp"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Redirected to slot &lt;span class="o"&gt;[&lt;/span&gt;12539] located at 192.168.1.184:6372
&lt;span class="go"&gt;OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent, everything works flawlessly, now we can connect to cluster from our app using 127.0.0.1 and any port from 6370 to 6375.&lt;/p&gt;

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

&lt;p&gt;We have covered the process of setting up Redis Cluster inside Docker and exposing it externally. By following the steps outlined, you should now have a solid understanding of how to deploy and manage Redis Cluster within a Docker environment.&lt;/p&gt;

&lt;p&gt;Additionally, we briefly touched upon Docker networking and discussed the limitations that can arise when using Docker on macOS. It's important to be aware of these limitations and consider alternative solutions if you encounter any issues specific to the macOS platform.&lt;/p&gt;

&lt;p&gt;By exploring these concepts and working with Docker, you have taken a deeper dive into containerization and gained practical knowledge that can be applied to other projects and scenarios.&lt;/p&gt;

&lt;p&gt;I hope this article has provided you with a clear understanding of Redis Cluster, Docker networking, and how to leverage these technologies together. Remember to keep experimenting and learning, as continuous improvement is key to mastering any technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://redis.io/docs/reference/cluster-spec/" rel="noopener noreferrer"&gt;https://redis.io/docs/reference/cluster-spec/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redis.io/docs/management/scaling/" rel="noopener noreferrer"&gt;https://redis.io/docs/management/scaling/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hub.docker.com/r/bitnami/redis-cluster/" rel="noopener noreferrer"&gt;https://hub.docker.com/r/bitnami/redis-cluster/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/network/" rel="noopener noreferrer"&gt;https://docs.docker.com/network/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redis.io/docs/ui/cli/" rel="noopener noreferrer"&gt;https://redis.io/docs/ui/cli/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>redis</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
