DEV Community

Cover image for Case Study: Deploying a Full-Stack MERN Application on AWS EC2 Using Docker & GitHub Actions
Opatola Bolaji Prince
Opatola Bolaji Prince

Posted on

Case Study: Deploying a Full-Stack MERN Application on AWS EC2 Using Docker & GitHub Actions

If you’ve ever built a full-stack app and asked yourself:

“How do I deploy this professionally in production with DevOps best practices?”

This case study walks through exactly how I achieved that — step-by-step — including the challenges, mistakes, and real fixes.

Project Overview I built and deployed a production-grade full-stack note-taking application called Notify.

Technologies involved:

  1. React + Vite for the frontend
  2. Node.js + Express for the backend
  3. MongoDB as the database
  4. Docker & Docker Compose for containerization
  5. AWS EC2 for hosting
  6. GitHub Actions CI/CD for automated deployment

The goal: Push code → App auto-deploys to AWS → No manual server changes

  • Phase 1 — Dockerizing the MERN Application The first step was packaging each part of the application into its own container.

Backend Dockerfile

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["npm", "start"]

Frontend Dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Enter fullscreen mode Exit fullscreen mode

The frontend is built and then served using Nginx — making it production-ready.

  • Phase 2 — Docker Compose for Multi-Service Orchestration To run all three containers together and ensure they communicate properly:
services:
  mongo:
    image: mongo:6
    container_name: mongo-db
    ports:
      - "27017:27017"
    restart: always

  backend:
    build: ./backend
    env_file: ./backend/.env
    environment:
      - MONGODB_URI=mongodb://mongo:27017/notifydb
    ports:
      - "5000:5000"
    depends_on:
      - mongo

  frontend:
    build: ./frontend
    ports:
      - "80:80"
    depends_on:
      - backend
Enter fullscreen mode Exit fullscreen mode

Key insight:
The backend connects to MongoDB using the container name mongo, not localhost.

  • Phase 3 — Deploying to AWS EC2
    I launched an Amazon Linux 2023 EC2 instance and prepared it for containerized deployment.

  • Installed Docker

  • Installed Docker Compose

  • Added ec2-user to Docker group

  • Security groups configured correctly

Opened inbound ports:

🎉 Site instantly reachable via:

  • Phase 4 — Enabling CI/CD With GitHub Actions The goal was zero manual deployments after setup.

📌 Every time I push to main:

  1. EC2 pulls new code
  2. Docker rebuilds
  3. Containers restart automatically
  4. Old images removed

GitHub Actions workflow:

name: Deploy to EC2

on:
  push:
    branches: ["main"]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: SSH Agent
        uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

      - name: Add Known Hosts
        run: ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts

      - name: Deploy Over SSH
        run: |
          ssh ec2-user@${{ secrets.EC2_HOST }} << 'EOF'
            cd ~/notify
            git pull origin main
            docker compose down || true
            docker compose up -d --build
            docker image prune -f
          EOF

Enter fullscreen mode Exit fullscreen mode
Enter fullscreen mode Exit fullscreen mode

Real-World Problems I Had to Solve
This wasn’t smooth — and that’s exactly the valuable part.

  • Problems + Fixes: Backend couldn’t connect to MongoDB → switched URI to container name

Deployment failing due to wrong Docker version → updated Docker + enabled compose

Frontend wouldn’t load → exposed port 80, not Vite’s local 5173

SSH denied → fixed key pairing and GitHub secrets

Docker commands failing → added EC2 user to Docker group

Every issue taught me lessons developers don’t learn until production ✨

You can watch the process here :
https://www.loom.com/share/897f49af1b274e3fba6921c842d0653b

🎯 What This Project Proved

I now understand:

🔥 Container orchestration with Docker
🔥 Cloud deployment using AWS EC2
🔥 Networking between services in production
🔥 Continuous Deployment pipelines
🔥 Debugging real infrastructure failures

This project took me from “I can code this” to:

✅ “I can run this in production — automatically.”

✅ Final Result ✅

✔ Full MERN App running live on AWS
✔ 100% Dockerized microservices
✔ Push-to-deploy automation
✔ Secure, scalable architecture
✔ Excellent DevOps experience gained

This is now a foundation I can reuse for multiple future projects

Top comments (0)