My Works

CI/CD Dockerized App Deployment on AWS

CI/CD Dockerized App Deployment on AWS

Overview

This portfolio website is a DevOps project deployed on an AWS EC2 instance using Docker, Docker Compose, and Nginx. It supports HTTPS via Let's Encrypt and automates deployments with GitHub Actions CI/CD.

Highlights

  • Dockerized React frontend using a multi-stage build
  • Nginx reverse proxy with Certbot for HTTPS
  • Container orchestration via Docker Compose
  • Hosted on an Ubuntu EC2 instance
  • Domain mapping via No-IP for stable access

Architecture Components

  • React App: Built using create-react-app and containerized.
  • Nginx: Serves app and handles SSL termination.
  • Certbot: Automatically issues and renews SSL certs.
  • Docker Compose: Manages service orchestration.
  • AWS EC2: Host environment with Ubuntu and Docker installed.

Directory Structure

my-portfolio/
ā”œā”€ā”€ app/                    # React app and Dockerfile
│   ā”œā”€ā”€ Dockerfile
│   ā”œā”€ā”€ src/
│   └── public/
ā”œā”€ā”€ reverse-proxy/         # Nginx + SSL setup
│   ā”œā”€ā”€ nginx.conf
│   └── init-letsencrypt.sh
ā”œā”€ā”€ docker-compose.yml     # Container orchestration

Network Flow

[User]
  ↓ HTTPS
[my-portfolio.zapto.org]
  ↓ DNS
[EC2 Ubuntu Instance]
  ↓ Reverse Proxy
[Nginx → React Container]

CI/CD Flow

[GitHub Push to main]
  ↓
[GitHub Actions Workflow]
  ↓
[Docker Build → Push Image to Docker Hub]
  ↓
[SSH into EC2 → Pull & Restart Container]

Deployment Steps

1. React App Dockerization

FROM node:18 as builder
WORKDIR /app
COPY . .
RUN npm ci && PUBLIC_URL=/ npm run build

FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html

2. Docker Compose Configuration

services:
  app:
    build: ./app
    container_name: react-app
    expose:
      - "80"

  nginx:
    image: nginx:latest
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./reverse-proxy/nginx.conf:/etc/nginx/conf.d/default.conf
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
    depends_on:
      - app

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
    entrypoint: sh -c
    command: >
      "certbot certonly --webroot
      --webroot-path=/var/lib/letsencrypt
      --email [email protected]
      --agree-tos
      --no-eff-email
      -d my-portfolio.zapto.org
      --rsa-key-size 4096"

volumes:
  certbot-etc:
  certbot-var:

3. Nginx Proxy Configuration

server {
  listen 80;
  server_name my-portfolio.zapto.org;
  location /.well-known/acme-challenge/ {
    root /var/lib/letsencrypt;
  }
  location / {
    return 301 https://$host$request_uri;
  }
}

server {
  listen 443 ssl;
  server_name my-portfolio.zapto.org;

  ssl_certificate /etc/letsencrypt/live/my-portfolio.zapto.org/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/my-portfolio.zapto.org/privkey.pem;

  location / {
    proxy_pass http://react-app:80;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

4. Certbot Initialization

  • Start nginx to respond to challenges
  • Run Certbot to generate cert
  • Mount cert volume to Nginx container

5. GitHub Actions CI/CD

Pipeline defined in .github/workflows/main.yml (not shown) builds, pushes, and deploys the container automatically.

6. Access

Live app at: https://my-portfolio.zapto.org

Skills Gained

  • Docker image building and orchestration with Compose
  • SSL automation using Certbot and Nginx
  • Domain management via dynamic DNS
  • CI/CD implementation using GitHub Actions
  • Server automation on AWS EC2