<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eryan Fauzan</title>
    <description>The latest articles on DEV Community by Eryan Fauzan (@eryanfauzan).</description>
    <link>https://dev.to/eryanfauzan</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%2F3862207%2F75b36dec-8024-421f-95ff-013295a5e002.jpg</url>
      <title>DEV Community: Eryan Fauzan</title>
      <link>https://dev.to/eryanfauzan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eryanfauzan"/>
    <language>en</language>
    <item>
      <title>[ID] Deploy NestJS ke AWS ECS Fargate Secara Manual: Cara Paling Simpel Buat Mulai</title>
      <dc:creator>Eryan Fauzan</dc:creator>
      <pubDate>Sun, 05 Apr 2026 13:56:18 +0000</pubDate>
      <link>https://dev.to/eryanfauzan/deploy-nestjs-ke-aws-ecs-fargate-secara-manual-cara-paling-simpel-buat-mulai-10am</link>
      <guid>https://dev.to/eryanfauzan/deploy-nestjs-ke-aws-ecs-fargate-secara-manual-cara-paling-simpel-buat-mulai-10am</guid>
      <description>&lt;p&gt;Ini artikel pertama gua di dev.to. Gua nulis ini karena makanan sehari-hari deploy NestJS ke ECS Fargate dan sebagai catetan gua juga di kala lupa.&lt;/p&gt;

&lt;p&gt;Gua bakal nulis dari pengalaman nyata setup yang gua pake di kerjaan, bukan setup ideal tapi work in production karena waktu dan tenaga yang terbatas.&lt;/p&gt;

&lt;p&gt;konten bahasa Indonesia yang ngebahas ini step by step jarang kayaknya ya wkwk atau terlalu niche ya? ajak-ajak gua lah kalo ada pembahasan terkait AWS khususnya DevOps dan WebDev. &lt;/p&gt;

&lt;p&gt;buat step by step gua bikin masih narrative tapi gua coba bahas sedetail mungkin, gambarnya bertahap gua lengkapin &lt;/p&gt;

&lt;p&gt;kemungkinan nantinya tulisan ini bakal ngelink ke beberapa tutorial lainnya dan aka di update bertahap.&lt;br&gt;
Semoga artikel ini ngebantu lo yang lagi di posisi yang sama.&lt;/p&gt;


&lt;h2&gt;
  
  
  Kenapa ECS Fargate, Bukan EC2?
&lt;/h2&gt;

&lt;p&gt;Kalau lo baru mau mulai deploy NestJS app ke AWS, pilihan pertama yang sering bikin bingung adalah: pakai EC2 atau ECS?&lt;/p&gt;

&lt;p&gt;EC2 itu simpel secara konsep, lo dapat satu virtual machine (temen gua beberapa ada yang kecele dia rada binggung saat gua bilang VM, gua bilang kayak semacam VPS baru nyambung), lo install Node.js, lo jalanin app lo. Tapi di balik itu lo harus urus sendiri: update OS, manage process dengan PM2, handle restart kalau app crash, dan kalau mau scale lo harus setup sendiri.&lt;/p&gt;

&lt;p&gt;ECS Fargate beda. Lo ga pegang server sama sekali. Lo cukup kasih container, define berapa CPU dan memory yang dibutuhkan, dan AWS yang urus sisanya kayak scheduling, restart, bahkan scaling kalau lo mau. Lo bayar per detik container jalan, bukan per jam instance hidup.&lt;/p&gt;

&lt;p&gt;Artikel ini bakal ngajarin lo cara deploy NestJS app ke ECS Fargate secara manual tanpa CI/CD dulu. Cukup dari local, push ke ECR, setup di console AWS, dan app lo jalan di belakang ALB.&lt;/p&gt;

&lt;p&gt;Ini cara yang paling gampang buat mulai, dan kalau lo udah paham flow-nya, nambah CI/CD belakangan jadi jauh lebih gampang.&lt;/p&gt;


&lt;h2&gt;
  
  
  Prasyarat
&lt;/h2&gt;

&lt;p&gt;Sebelum mulai, pastiin lo udah punya hal-hal berikut:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Side:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS account yang udah aktif&lt;/li&gt;
&lt;li&gt;IAM user dengan akses ke ECR dan ECS &lt;strong&gt;(jangan pakai root account)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ECR repository yang udah dibuat&lt;/li&gt;
&lt;li&gt;RDS PostgreSQL instance yang udah jalan di VPC yang sama&lt;/li&gt;
&lt;li&gt;ALB yang udah di-setup atau siap di-setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Local Side:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI v2 terinstall dan sudah di-configure (&lt;code&gt;aws configure&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Docker Desktop terinstall dan jalan&lt;/li&gt;
&lt;li&gt;NestJS app yang udah siap di-deploy&lt;/li&gt;
&lt;li&gt;Git (karena script deploy kita bakal pakai branch name dan commit hash buat image tag)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;VPC dengan minimal 2 public subnet untuk ALB&lt;/li&gt;
&lt;li&gt;Security group yang udah direncanain ECS task perlu bisa terima traffic dari ALB, dan bisa konek ke RDS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kalau semua udah siap, kita mulai dari langkah pertama.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: Dockerize NestJS App
&lt;/h2&gt;

&lt;p&gt;Buat &lt;code&gt;Dockerfile&lt;/code&gt; di root project lo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---------- builder stage ----------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:22-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/app&lt;/span&gt;

&lt;span class="c"&gt;# install build deps (include dev deps because we need tsc)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="c"&gt;# copy source &amp;amp; build&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;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# ---------- production stage ----------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:22-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/app&lt;/span&gt;

&lt;span class="c"&gt;# copy only production deps and app metadata&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="c"&gt;# install only production deps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="c"&gt;# copy built artifacts from builder&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /usr/app/dist ./dist&lt;/span&gt;

&lt;span class="c"&gt;# create non-root user (optional but recommended)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;-S&lt;/span&gt; app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;-S&lt;/span&gt; app &lt;span class="nt"&gt;-G&lt;/span&gt; app

&lt;span class="c"&gt;# create exports directory with proper permissions&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /usr/app/exports &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; app:app /usr/app

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

&lt;span class="c"&gt;# GANTI INI -- sesuaikan dengan port NestJS app lo&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "dist/main.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beberapa hal yang perlu diperhatiin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pakai &lt;strong&gt;multi-stage build&lt;/strong&gt;. Stage pertama buat build, stage kedua buat production image. Hasilnya image lebih kecil karena ga bawa devDependencies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node:22-alpine&lt;/code&gt; versi LTS terbaru, lebih ringan dari image full.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm ci --omit=dev&lt;/code&gt; lebih modern dari &lt;code&gt;--only=production&lt;/code&gt;, install hanya production dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-root user&lt;/strong&gt; ini security best practice yang sering dilewatin. Container lo ga jalan sebagai root, jadi kalau ada exploit, dampaknya lebih terbatas.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mkdir exports&lt;/code&gt; kalau app lo ada fitur generate file, direktori ini udah siap dengan permission yang bener.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test dulu di local sebelum push ke ECR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; nama-app-lo &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 nama-app-lo &lt;span class="c"&gt;# GANTI INI - sesuaikan port&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Setup ECR dan Push Image
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Buat ECR Repository
&lt;/h3&gt;

&lt;p&gt;Masuk ke AWS Console &amp;gt; ECR &amp;gt; Create repository. Kasih nama yang deskriptif, misalnya &lt;code&gt;nama-project/nama-service&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;kalo mau liat push commandnya tinggal klik repository yang udah dibuat terus klik disana ada button "View Push Command"&lt;/p&gt;

&lt;p&gt;tapi gua ada cara lebih gampang lagi biar ga berkali-kali push command kalo mau push image&lt;/p&gt;




&lt;h3&gt;
  
  
  Script Deploy ke ECR
&lt;/h3&gt;

&lt;p&gt;Ini script yang gua pake buat build dan push image dari local ke 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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Pastiin AWS CLI ada di PATH lo&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/c/Program Files/Amazon/AWSCLIV2:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&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;"Using AWS CLI from: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;which aws.exe&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Variables&lt;/span&gt;
&lt;span class="nv"&gt;BRANCH_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--abbrev-ref&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="o"&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;BRANCH_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;LAST_COMMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"%h - %s (%cr) &amp;lt;%an&amp;gt;"&lt;/span&gt;&lt;span class="si"&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;"ap-southeast-1"&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI - sesuaikan region lo&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;"YOUR_ACCOUNT_ID.dkr.ecr.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.amazonaws.com/YOUR_REPO_NAME"&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;

&lt;span class="c"&gt;# Display deployment info&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Deployment Information ==="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Branch: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BRANCH_NAME&lt;/span&gt;&lt;span class="k"&gt;}&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;"Commit: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LAST_COMMIT&lt;/span&gt;&lt;span class="k"&gt;}&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 Tag: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="k"&gt;}&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;"================================"&lt;/span&gt;

&lt;span class="c"&gt;# Authenticate Docker ke ECR&lt;/span&gt;
aws.exe ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&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;
  | 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="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ECR_URI&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Build &amp;amp; tag&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; nama-app-lo &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;
docker tag nama-app-lo:latest &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ECR_URI&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:latest"&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;
docker tag nama-app-lo:latest &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ECR_URI&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;IMAGE_TAG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;

&lt;span class="c"&gt;# Push&lt;/span&gt;
docker push &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ECR_URI&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:latest"&lt;/span&gt;
docker push &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ECR_URI&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;IMAGE_TAG&lt;/span&gt;&lt;span class="k"&gt;}&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;"Deployed image tag: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kenapa gua push dua tag sekaligus?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;latest&lt;/code&gt; yang direferensiin di ECS task definition, jadi tiap deploy ECS tinggal pull yang terbaru&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;branch-commithash&lt;/code&gt; buat versioning, kalau ada masalah lo tau persis commit mana yang jalan di production&lt;/li&gt;
&lt;li&gt;ini kepake banget saat lo nantinya di minta buat pisahan enviroment misalnya development, staging dan production lo ga binggung enviroment mana pake image yang mana dan branch. karena real casenya dev, stag, prod itu beda-beda semua enviromentnya saat ada keterbatasan team ini bisa nolong lo dari kepusingan itu&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Simpan .env di S3
&lt;/h2&gt;

&lt;p&gt;Ini bagian yang jarang dibahas tapi sangat praktis. Daripada hardcode environment variable satu-satu di task definition, lo bisa simpan &lt;code&gt;.env&lt;/code&gt; file di S3 dan ECS yang bakal load otomatis waktu container start.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kenapa S3?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Lebih gampang di-manage, tinggal update file di S3, ga perlu update task definition&lt;/li&gt;
&lt;li&gt;Lebih aman, file ada di S3 dengan permission yang bisa dikontrol&lt;/li&gt;
&lt;li&gt;Familiar, format &lt;code&gt;.env&lt;/code&gt; yang sama kayak di local&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cara Setup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Buat S3 bucket khusus buat env files:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nama-bucket-lo/
  nama-app-lo/
    staging/
      .env
    production/
      .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Upload .env ke S3:&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;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; .env s3://nama-bucket-lo/nama-app-lo/staging/.env &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Kasih permission ke ECS Task Execution Role:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ECS butuh permission buat baca file dari S3. Tambahkan policy ini ke &lt;code&gt;ecsTaskExecutionRole&lt;/code&gt; di IAM:&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;"s3:GetObject"&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;"arn:aws:s3:::nama-bucket-lo/*"&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;"s3:GetBucketLocation"&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;"arn:aws:s3:::nama-bucket-lo"&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;
  
  
  Step 4: Setup Task Definition
&lt;/h2&gt;

&lt;p&gt;Task definition itu blueprint container lo di ECS. Define image mana yang dipakai, berapa resource yang dialokasiin, port mana yang dibuka, dan dari mana env-nya diload.&lt;/p&gt;

&lt;p&gt;Ini contoh task definition yang gua pake:&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;"nama-service-lo"&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;"nama-service-lo"&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_ACCOUNT_ID.dkr.ecr.ap-southeast-1.amazonaws.com/YOUR_REPO:latest"&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"portMappings"&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;"containerPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"hostPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tcp"&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;"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="nl"&gt;"environmentFiles"&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;"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;"arn:aws:s3:::nama-bucket-lo/nama-app-lo/staging/.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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3"&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/nama-service-lo"&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;"ap-southeast-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"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="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_ID:role/ecsTaskExecutionRole"&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;"256"&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;"512"&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;Beberapa hal yang perlu diperhatiin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cpu: 256&lt;/code&gt; dan &lt;code&gt;memory: 512&lt;/code&gt; itu spec paling minimal di Fargate, dan perlu diingat ini dialokasiin di level &lt;strong&gt;task&lt;/strong&gt;, bukan per container. Artinya kalau lo punya lebih dari satu container di task yang sama, semua sharing pool ini. Untuk single container staging cukup, tapi kalau lo mau nambah container lain di task yang sama, naikin spec dulu sebelum deploy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;networkMode: awsvpc&lt;/code&gt; mandatory buat Fargate, setiap task dapat ENI sendiri.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logDriver: awslogs&lt;/code&gt; log container lo otomatis masuk ke CloudWatch, sangat membantu waktu debugging.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;environmentFiles&lt;/code&gt; yang nge-load &lt;code&gt;.env&lt;/code&gt; dari S3 yang kita setup di step sebelumnya.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Buat CloudWatch log group dulu sebelum deploy. Ada dua cara:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Via AWS CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Buat log group&lt;/span&gt;
aws logs create-log-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--log-group-name&lt;/span&gt; /ecs/nama-service-lo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;

&lt;span class="c"&gt;# Set retention 30 hari supaya ga numpuk dan kena billing&lt;/span&gt;
aws logs put-retention-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--log-group-name&lt;/span&gt; /ecs/nama-service-lo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--retention-in-days&lt;/span&gt; 30 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Via AWS Console:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Masuk ke CloudWatch &amp;gt; Log groups &amp;gt; Create log group. Isi log group name dengan &lt;code&gt;/ecs/nama-service-lo&lt;/code&gt;, lalu di bagian Retention setting pilih &lt;strong&gt;30 days&lt;/strong&gt;. Kalau lo skip retention policy, log lo bakal disimpan selamanya dan kena billing terus.&lt;/p&gt;




&lt;h3&gt;
  
  
  Buat Cluster
&lt;/h3&gt;

&lt;p&gt;Masuk ke AWS Console &amp;gt; ECS &amp;gt; Clusters &amp;gt; Create Cluster. Pilih &lt;strong&gt;AWS Fargate&lt;/strong&gt; sebagai infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Setup ALB dan Target Group
&lt;/h2&gt;

&lt;p&gt;Sebelum buat service, lo harus siapin ALB dan target group dulu karena lo bakal attach ini waktu buat service di Step selanjutnya.&lt;/p&gt;

&lt;h3&gt;
  
  
  Buat Target Group
&lt;/h3&gt;

&lt;p&gt;Masuk ke EC2 &amp;gt; Target Groups &amp;gt; Create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Target type:&lt;/strong&gt; IP (bukan Instance, karena Fargate pakai awsvpc mode)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; HTTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port:&lt;/strong&gt; sesuaikan dengan port container lo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health check path:&lt;/strong&gt; &lt;code&gt;/&lt;/code&gt; atau endpoint health check lo kalau ada (biasanya gua pake pattern&lt;code&gt;/api/v1/health&lt;/code&gt;) isinya json biasa aja&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Buat ALB
&lt;/h3&gt;

&lt;p&gt;Masuk ke EC2 &amp;gt; Load Balancers &amp;gt; Create &amp;gt; Application Load Balancer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scheme:&lt;/strong&gt; Internet-facing (kalau mau diakses dari luar)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subnets:&lt;/strong&gt; minimal 2 AZ, pilih public subnet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security group:&lt;/strong&gt; allow inbound port 80 dan/atau 443&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listener:&lt;/strong&gt; forward ke target group yang baru lo buat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setelah ALB dan target group siap, baru lo buat service ECS-nya.&lt;/p&gt;

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

&lt;p&gt;Masuk ke cluster yang baru lo buat, klik &lt;strong&gt;Create service&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Isi konfigurasi dasar:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launch type:&lt;/strong&gt; Fargate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task definition:&lt;/strong&gt; pilih yang lo buat di Step 4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service name:&lt;/strong&gt; kasih nama yang deskriptif&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desired tasks:&lt;/strong&gt; 1 untuk awal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Di bagian &lt;strong&gt;Networking:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Pilih VPC yang sama dengan RDS lo&lt;/li&gt;
&lt;li&gt;Pilih minimal 2 subnet&lt;/li&gt;
&lt;li&gt;Buat atau pilih security group yang allow inbound dari ALB dan allow outbound ke RDS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Di bagian &lt;strong&gt;Load balancing:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Pilih &lt;strong&gt;Application Load Balancer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Pilih ALB yang baru lo buat&lt;/li&gt;
&lt;li&gt;Pilih target group yang sesuai&lt;/li&gt;
&lt;li&gt;Pastiin health check path lo bener&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Klik &lt;strong&gt;Create service&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ECS bakal langsung pull image dari ECR dan jalanin task pertama lo. Setelah task &lt;code&gt;RUNNING&lt;/code&gt;, ECS otomatis register ke target group dan ALB mulai routing traffic ke container lo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Deploy Ulang Setelah Setup Awal
&lt;/h2&gt;

&lt;p&gt;Step 5 dan 6 itu cuma dilakuin sekali waktu pertama kali setup. Setelah infrastruktur jalan, workflow deploy lo jadi jauh lebih simpel.&lt;/p&gt;

&lt;p&gt;Tiap kali ada update code, lo cukup:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Push image baru ke ECR:&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;./deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Trigger ECS buat pull image terbaru:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&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;"ap-southeast-1"&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI&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;"nama-cluster-lo"&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"nama-service-lo"&lt;/span&gt; &lt;span class="c"&gt;# GANTI INI&lt;/span&gt;

aws.exe ecs update-service &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="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&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;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"&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="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE&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;--force-new-deployment&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Service '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' updated"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--force-new-deployment&lt;/code&gt; yang bikin ECS stop task lama dan start task baru dengan image &lt;code&gt;latest&lt;/code&gt; yang baru aja lo push. Script ini cuma bisa dijalanin kalau service lo udah exist, makanya setup awal di Step 5 harus selesai dulu.&lt;/p&gt;

&lt;p&gt;Dua script, selesai.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Verifikasi
&lt;/h2&gt;

&lt;p&gt;Setelah &lt;code&gt;update-service&lt;/code&gt; jalan, tunggu beberapa menit (biasanya 2-5 menit) dan cek:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ECS Console&lt;/strong&gt; &amp;gt; cluster lo &amp;gt; service &amp;gt; Tasks, pastiin task statusnya &lt;code&gt;RUNNING&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Logs&lt;/strong&gt;, cek &lt;code&gt;/ecs/nama-service-lo&lt;/code&gt; buat liat log container lo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target Group&lt;/strong&gt;, cek health check statusnya &lt;code&gt;healthy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ALB DNS&lt;/strong&gt;, akses DNS ALB lo di browser, harusnya app lo udah jalan&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kalau task gagal start, logs di task atau logs CloudWatch itu tempat pertama yang harus lo cek. keterangan log nya itu sama persis ketika lo jalanin di lokal seperti log error, warning, info dll&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Beberapa hal yang gua pelajarin dari setup ini di production:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Health check itu krusial&lt;/strong&gt;&lt;br&gt;
Kalau health check path lo salah atau endpoint lo return status bukan 200, ALB bakal terus-terusan drain dan replace task dan ini yang pertama kali gua pikir selalu gagal 2 hari debugging ini ternyata dia nyari yang response 200. Pastiin health check path lo bener sebelum deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Security group harus teliti&lt;/strong&gt;&lt;br&gt;
ECS task, ALB, dan RDS punya security group masing-masing. Yang paling sering bikin masalah: ECS task ga bisa konek ke RDS karena security group RDS belum allow inbound dari security group ECS. Cek ini kalau app lo gagal konek ke database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. S3 env file ga otomatis refresh&lt;/strong&gt;&lt;br&gt;
ini juga pain point, Kalau lo update &lt;code&gt;.env&lt;/code&gt; di S3, container yang lagi jalan ga otomatis reload. Lo tetap harus trigger &lt;code&gt;--force-new-deployment&lt;/code&gt; buat apply perubahan env.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Minimal spec dulu&lt;/strong&gt;&lt;br&gt;
Mulai dari 256 CPU / 512 MB memory. Monitor dulu di CloudWatch, baru scale up kalau emang butuh. Jangan langsung pakai spec besar karena Fargate billing per resource yang lo define.&lt;/p&gt;




&lt;h2&gt;
  
  
  Penutup
&lt;/h2&gt;

&lt;p&gt;Setup ini mungkin keliatan banyak langkahnya, tapi setelah semua infrastruktur jalan, workflow deploy harian lo cuma dua script. Simpel dan bisa dikontrol.&lt;/p&gt;

&lt;p&gt;Dari sini, langkah selanjutnya yang natural adalah:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tambah CI/CD supaya ga perlu build dari local (AWS CodeBuild)&lt;/li&gt;
&lt;li&gt;Setup auto scaling di ECS service&lt;/li&gt;
&lt;li&gt;Pisahin staging dan production environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kalau ada pertanyaan atau ada yang kurang jelas, drop di kolom komentar. Gua bakal jawab sesuai pengalaman gua.&lt;/p&gt;

</description>
      <category>ecs</category>
      <category>aws</category>
      <category>nestjs</category>
      <category>indonesia</category>
    </item>
  </channel>
</rss>
