MERN

Deploying a MERN App to AWS with Docker & GitHub Actions

dev.prakah2011 February 8, 2025 3 min read
🍃

A modern CI/CD pipeline removes the anxiety from deployments. In this guide we’ll containerise a MERN application with Docker, push images to Amazon ECR, and deploy to ECS Fargate using a GitHub Actions workflow — automated, zero-downtime, on every push to main.

Project Structure

mern-app/
  backend/
    Dockerfile
    src/
  frontend/
    Dockerfile
    src/
  docker-compose.yml     ← local development
  .github/
    workflows/
      deploy.yml

1. Multi-Stage Dockerfiles

# backend/Dockerfile
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY src ./src
EXPOSE 4000
USER node
CMD ["node", "src/index.js"]
# frontend/Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine AS runner
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

2. Local Development with Docker Compose

services:
  mongo:
    image: mongo:7
    volumes:
      - mongo-data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: secret

  backend:
    build: ./backend
    ports:
      - "4000:4000"
    environment:
      MONGO_URI: mongodb://root:secret@mongo:27017/myapp?authSource=admin
    depends_on:
      - mongo

  frontend:
    build: ./frontend
    ports:
      - "3000:80"
    depends_on:
      - backend

volumes:
  mongo-data:

3. GitHub Actions Deploy Workflow

# .github/workflows/deploy.yml
name: Deploy to AWS ECS

on:
  push:
    branches: [main]

env:
  AWS_REGION: us-east-1
  ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - 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 ECR
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag, push backend
        run: |
          docker build -t $ECR_REGISTRY/mern-backend:${{ github.sha }} ./backend
          docker push $ECR_REGISTRY/mern-backend:${{ github.sha }}

      - name: Build, tag, push frontend
        run: |
          docker build -t $ECR_REGISTRY/mern-frontend:${{ github.sha }} ./frontend
          docker push $ECR_REGISTRY/mern-frontend:${{ github.sha }}

      - name: Deploy to ECS
        run: |
          aws ecs update-service \
            --cluster mern-cluster \
            --service mern-backend \
            --force-new-deployment
          aws ecs update-service \
            --cluster mern-cluster \
            --service mern-frontend \
            --force-new-deployment

4. Zero-Downtime with ECS Rolling Updates

Configure your ECS service with a rolling update deployment type and a minimum healthy percent of 100% — ECS will launch new tasks before draining old ones, giving you zero-downtime deploys automatically.

aws ecs create-service \
  --cluster mern-cluster \
  --service-name mern-backend \
  --deployment-configuration "minimumHealthyPercent=100,maximumPercent=200" \
  --deployment-controller type=ECS

With this pipeline, every git push to main triggers a full build, test, and deploy in about 4 minutes. Your team can ship confidently multiple times per day.

dev.prakah2011
dev.prakah2011

Developer & author at DevForge Agency.

Related Articles

🍃
MERN

Building Real-Time Apps with MERN Stack & Socket.io