Jihun's Full Deployment Guide: Dockerized MERN App to EC2 via GitLab CI/CD

This guide walks through all steps to deploy a full-stack MERN app (MongoDB, Express, React, Node.js) on an EC2 instance using Docker, GitLab CI/CD, and SSH keys.

  1. Launch EC2 Instance

    • Go to AWS EC2 console
    • Launch an Ubuntu instance (e.g., Ubuntu 22.04 t2.micro)
    • Create and download a new key pair (e.g., EC2-Tutorial.pem)
    • Allow inbound ports: 22 (SSH), 3000 (Frontend), 5000 (Backend), 27017 (Mongo)
  2. SSH into EC2

    chmod 400 EC2-Tutorial.pem
    ssh -i /path/to/EC2-Tutorial.pem ubuntu@<Public_IP>
  3. Install Docker & Docker Compose

    sudo apt update && sudo apt install docker.io docker-compose -y
    sudo usermod -aG docker $USER
    newgrp docker
  4. Set Up GitLab Repo

    • Create new repo on GitLab
    • Clone locally:
    git clone git@gitlab.com:your-username/networth-Tracker.git
  5. Generate SSH Key for GitLab

    On your local machine:

    ssh-keygen -t rsa -b 4096 -C "gitlab-deploy"
    # Save as ~/.ssh/gitlab-deploy

    Add to GitLab

    • Go to GitLab > Settings > CI/CD > Variables
    • Add SSH_PRIVATE_KEY with contents of ~/.ssh/gitlab-deploy

    Add to EC2

    cat ~/.ssh/gitlab-deploy.pub # Copy this
    ssh -i EC2-Tutorial.pem ubuntu@<EC2_IP>
    echo "<paste_here>" >> ~/.ssh/authorized_keys
  6. Prepare Project

    Ensure structure:

    networth-Tracker/
    ├── backend/
    │   ├── Dockerfile
    │   ├── .env
    │   └── ...
    ├── frontend/
    │   └── Dockerfile
    ├── docker-compose.yml
    └── .gitlab-ci.yml
  7. Sample docker-compose.yml

    services:
      frontend:
        build: ./frontend
        ports:
          - "3000:80"
        depends_on:
          - backend
      backend:
        build: ./backend
        ports:
          - "5000:5000"
        env_file:
          - ./backend/.env
        depends_on:
          - mongo
      mongo:
        image: mongo
        ports:
          - "27017:27017"
  8. Sample .gitlab-ci.yml

    stages:
      - deploy
    
    deploy_to_ec2:
      stage: deploy
      before_script:
        - "which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )"
        - eval $(ssh-agent -s)
        - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
        - mkdir -p ~/.ssh
        - chmod 700 ~/.ssh
        - ssh-keyscan -H <Elastic_IP> >> ~/.ssh/known_hosts
      script:
        - ssh ubuntu@<Elastic_IP> "cd ~/networth-Tracker && git pull && docker-compose down && docker-compose up -d --build"
      only:
        - main
  9. Set Up Elastic IP (Optional but recommended)

    • AWS EC2 > Elastic IPs > Allocate new
    • Associate it with your instance
    • Replace <Elastic_IP> in GitLab and local SSH commands
  10. Deploy

    • Push code to GitLab:
    git add .
    git commit -m "Initial commit"
    git push origin main
    • GitLab will trigger deployment
    • App available at http://<Elastic_IP>:3000
  11. Common Errors & Troubleshooting

    • Disk Space Full (No space left on device): Run docker system prune -af on the EC2 instance to free up space.
    • SSH Permission Denied: Ensure the correct public key is in EC2’s ~/.ssh/authorized_keys and GitLab’s private key is valid.
    • Pipeline Still Using Old IP: Double-check that .gitlab-ci.yml has been committed and pushed with the new IP. Trigger a fresh pipeline, not just a retry.
    • Docker Container Name Conflict: If a container already exists, remove it using docker rm <container-id> or docker-compose down.
    • Outdated Git Reference Error: If git pull fails due to ref mismatch, run git fetch --all && git reset --hard origin/main on the EC2.
    • App Not Working After EC2 Reboot: You must manually SSH into EC2 and run docker-compose up -d or set up a systemd service or cron job to restart Docker on reboot.
    • localhost URLs: Replace any hardcoded http://localhost with your actual Elastic IP when deployed.