๐ Multi-Application CI/CD on AWS (Production-Style)
Source: Dev.to
๐ฏ Project Goal
Host multiple web applications on AWS where: ๐๏ธ High-Level Architecture ๐ Repository Structure Each app is independent but deployed via same pipeline logic. ๐ STEP 1: AWS ACCOUNT & IAM
Create IAM User (DevOpsUser)
Permissions: โ ๏ธ Create Access Key (Programmatic) ๐ STEP 2: Networking (VPC) ๐ฆ STEP 3: Create ECR Repositories
Create one ECR per app: ๐ณ STEP 4: Dockerize Applications
Example Dockerfile (Node.js / Python) โ๏ธ STEP 5: ECS Cluster (Fargate) ๐ STEP 6: Application Load Balancer
Create ALB
Listener:
HTTP โ Redirect HTTPS
HTTPS โ Target Groups
Path-based routing: ๐ STEP 7: GitHub Secrets
In GitHub Repo โ Settings โ Secrets
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_REGION
AWS_ACCOUNT_ID
ECR_REPO_APP1
ECR_REPO_APP2 ๐ STEP 8: GitHub Actions CI/CD Pipeline
.github/workflows/deploy.yml โ Repeat build steps for app2 & app3. ๐ STEP 9: Monitoring & Logs
Enable CloudWatch Logs in ECS Task Definition
View: ๐ STEP 10: Security & Best Practices
โ HTTPS using ACM
โ IAM least privilege
โ Secrets in GitHub (not code)
โ Private ECR
โ Auto-scaling ๐งช STEP 11: Testing Flow Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
Developer | v
GitHub Repo (App1, App2, App3) |
GitHub Actions (CI/CD) | |-- Test |-- Docker Build |-- Push to ECR v
AWS ECR (Images) |
AWS ECS (Fargate) |
Application Load Balancer | v
End Users (HTTPS) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Developer | v
GitHub Repo (App1, App2, App3) |
GitHub Actions (CI/CD) | |-- Test |-- Docker Build |-- Push to ECR v
AWS ECR (Images) |
AWS ECS (Fargate) |
Application Load Balancer | v
End Users (HTTPS) CODE_BLOCK:
Developer | v
GitHub Repo (App1, App2, App3) |
GitHub Actions (CI/CD) | |-- Test |-- Docker Build |-- Push to ECR v
AWS ECR (Images) |
AWS ECS (Fargate) |
Application Load Balancer | v
End Users (HTTPS) CODE_BLOCK:
multi-app-devops/
โโโ app1/
โ โโโ Dockerfile
โ โโโ src/
โโโ app2/
โ โโโ Dockerfile
โ โโโ src/
โโโ app3/
โ โโโ Dockerfile
โ โโโ src/
โโโ .github/ โโโ workflows/ โโโ deploy.yml Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
multi-app-devops/
โโโ app1/
โ โโโ Dockerfile
โ โโโ src/
โโโ app2/
โ โโโ Dockerfile
โ โโโ src/
โโโ app3/
โ โโโ Dockerfile
โ โโโ src/
โโโ .github/ โโโ workflows/ โโโ deploy.yml CODE_BLOCK:
multi-app-devops/
โโโ app1/
โ โโโ Dockerfile
โ โโโ src/
โโโ app2/
โ โโโ Dockerfile
โ โโโ src/
โโโ app3/
โ โโโ Dockerfile
โ โโโ src/
โโโ .github/ โโโ workflows/ โโโ deploy.yml CODE_BLOCK:
app1-ecr
app2-ecr
app3-ecr Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
app1-ecr
app2-ecr
app3-ecr CODE_BLOCK:
app1-ecr
app2-ecr
app3-ecr CODE_BLOCK:
123456789012.dkr.ecr.us-east-1.amazonaws.com/app1 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
123456789012.dkr.ecr.us-east-1.amazonaws.com/app1 CODE_BLOCK:
123456789012.dkr.ecr.us-east-1.amazonaws.com/app1 CODE_BLOCK:
FROM node:18-alpine
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"] Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
FROM node:18-alpine
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"] CODE_BLOCK:
FROM node:18-alpine
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"] CODE_BLOCK:
/app1 โ app1-service
/app2 โ app2-service
/app3 โ app3-service Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
/app1 โ app1-service
/app2 โ app2-service
/app3 โ app3-service CODE_BLOCK:
/app1 โ app1-service
/app2 โ app2-service
/app3 โ app3-service CODE_BLOCK:
name: CI-CD Pipeline on: push: branches: [ "main" ] env: AWS_REGION: us-east-1 jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR run: | aws ecr get-login-password --region $AWS_REGION \ | docker login --username AWS --password-stdin \ ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - name: Build & Push App1 run: | docker build -t app1 ./app1 docker tag app1:latest ${{ secrets.ECR_REPO_APP1 }}:latest docker push ${{ secrets.ECR_REPO_APP1 }}:latest - name: Deploy to ECS run: | aws ecs update-service \ --cluster devops-cluster \ --service app1-service \ --force-new-deployment Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
name: CI-CD Pipeline on: push: branches: [ "main" ] env: AWS_REGION: us-east-1 jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR run: | aws ecr get-login-password --region $AWS_REGION \ | docker login --username AWS --password-stdin \ ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - name: Build & Push App1 run: | docker build -t app1 ./app1 docker tag app1:latest ${{ secrets.ECR_REPO_APP1 }}:latest docker push ${{ secrets.ECR_REPO_APP1 }}:latest - name: Deploy to ECS run: | aws ecs update-service \ --cluster devops-cluster \ --service app1-service \ --force-new-deployment CODE_BLOCK:
name: CI-CD Pipeline on: push: branches: [ "main" ] env: AWS_REGION: us-east-1 jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR run: | aws ecr get-login-password --region $AWS_REGION \ | docker login --username AWS --password-stdin \ ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - name: Build & Push App1 run: | docker build -t app1 ./app1 docker tag app1:latest ${{ secrets.ECR_REPO_APP1 }}:latest docker push ${{ secrets.ECR_REPO_APP1 }}:latest - name: Deploy to ECS run: | aws ecs update-service \ --cluster devops-cluster \ --service app1-service \ --force-new-deployment - Developers push code to GitHub
- GitHub Actions automatically:
- Builds Docker images
- Pushes images to AWS ECR
- Deploys to AWS ECS (Fargate) or EC2 + Docker
- End users access apps via HTTPS (ALB + ACM)
- Logs & monitoring via CloudWatch
- Supports rolling / blue-green deployment - AmazonEC2FullAccess
- AmazonECS_FullAccess
- AmazonEC2ContainerRegistryFullAccess
- CloudWatchFullAccess
- IAMReadOnlyAccess - 2 Public Subnets
- Internet Gateway
- Route Table
- Security Groups:
- ALB: 80, 443
- ECS: 3000/5000/80 (app ports) - AWS Account ID
- Repository URI
Example: - Create ECS Cluster
- Create Task Definition per App - Container Image: ECR URI
- Port Mapping
- CPU & Memory - Launch Type: FARGATE
- Desired Tasks: 2 (HA) - Health checks - Developer changes code
- Push to main
- GitHub Action triggers
- Docker image rebuilt
- Image pushed to ECR
- ECS pulls new image
- Rolling deployment
- End user sees updated app